1# -*- tcl -*-
2# statistics.test --
3#    Test cases for the ::math::statistics package
4#
5# Note:
6#    The tests assume tcltest 2.1, in order to compare
7#    floating-point results
8
9# -------------------------------------------------------------------------
10
11source [file join \
12	[file dirname [file dirname [file join [pwd] [info script]]]] \
13	devtools testutilities.tcl]
14
15testsNeedTcl     8.5;# statistics,linalg!
16testsNeedTcltest 2.1
17
18support {
19    useLocal math.tcl math
20    useLocal linalg.tcl math::linearalgebra
21    useLocal optimize.tcl math::optimize
22}
23testing {
24    useLocal statistics.tcl math::statistics
25}
26
27# -------------------------------------------------------------------------
28
29set ::data_uniform  [list 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0]
30set ::data_missing  [list 1.0 1.0 1.0 {} 1.0 {} {} 1.0 1.0 1.0 1.0 1.0 1.0]
31set ::data_linear   [list 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0]
32set ::data_empty    [list {} {} {}]
33set ::data_missing2 [list 1.0 2.0 3.0 {} 4.0 5.0 6.0 7.0 8.0 9.0 10.0]
34
35#
36# Create and register (in that order!) custom matching procedures
37#
38proc matchTolerant { expected actual } {
39   set match 1
40   foreach a $actual e $expected {
41      if { abs($e-$a)>0.0001*abs($e) &&
42           abs($e-$a)>0.0001*abs($a)     } {
43         set match 0
44         break
45      }
46   }
47   return $match
48}
49proc matchTolerant2 { expected actual } {
50   set match 1
51   foreach a $actual e $expected {
52      if { abs($e-$a)>0.025*abs($e) &&
53           abs($e-$a)>0.025*abs($a)     } {
54         set match 0
55         break
56      }
57   }
58   return $match
59}
60proc matchAlmostZero { expected actual } {
61   set match 1
62   foreach a $actual {
63      if { abs($a)>1.0e-6 } {
64         set match 0
65         break
66      }
67   }
68   return $match
69}
70customMatch tolerant   matchTolerant
71customMatch tolerant2  matchTolerant2
72customMatch almostzero matchAlmostZero
73
74#
75# Test cases
76#
77test "BasicStats-1.0" "Basic statistics - uniform data" -match tolerant -body {
78  set all_data [::math::statistics::BasicStats all $::data_uniform]
79} -result [list 1.0 1.0 1.0 [llength $::data_uniform] 0.0 0.0 0.0 0.0]
80
81test "BasicStats-1.1" "Basic statistics - empty data" -match glob -body {
82  catch {
83     set all_data [::math::statistics::BasicStats all $::data_empty]
84  } msg
85  set msg
86} -result "Too*"
87
88#
89# Result must be the same as for 1.0! Hence ::data_empty and ::data_uniform
90#
91test "BasicStats-1.2" "Basic statistics - missing data" -match tolerant -body {
92  set all_data [::math::statistics::BasicStats all $::data_missing]
93} -result [list 1.0 1.0 1.0 [llength $::data_uniform] 0.0 0.0 0.0 0.0]
94
95test "BasicStats-1.3" "Basic statistics - linear data - mean" -match tolerant -body {
96  set value [::math::statistics::mean $::data_linear]
97} -result 5.5
98
99test "BasicStats-1.4" "Basic statistics - linear data - min" -match tolerant  -body {
100  set value [::math::statistics::min $::data_linear]
101} -result 1.0
102
103test "BasicStats-1.5" "Basic statistics - linear data - max" -match tolerant  -body {
104  set value [::math::statistics::max $::data_linear]
105} -result 10.0
106
107test "BasicStats-1.6" "Basic statistics - linear data - number" -match tolerant  -body {
108  set value [::math::statistics::number $::data_linear]
109} -result 10
110
111test "BasicStats-1.7" "Basic statistics - missing data - number" -match tolerant  -body {
112  set value [::math::statistics::number $::data_missing2]
113} -result 10
114
115test "BasicStats-1.8" "Basic statistics - missing data - stdev" -match almostzero -body {
116  set value1 [::math::statistics::stdev  $::data_linear]
117  set value2 [::math::statistics::stdev  $::data_missing2]
118  expr {abs($value1-$value2)}
119} -result 0.001 ;# Zero is impossible
120
121test "BasicStats-1.9" "Basic statistics - missing data - var" -match almostzero -body {
122  set value1 [::math::statistics::stdev  $::data_linear]
123  set value2 [::math::statistics::var    $::data_missing2]
124  expr {$value1*$value1-$value2}
125} -result 0.001 ;# Zero is impossible
126
127test "BasicStats-1.10" "Basic statistics - missing data - pstdev" -match almostzero -body {
128  set value1 [::math::statistics::pstdev  $::data_linear]
129  set value2 [::math::statistics::pstdev  $::data_missing2]
130  expr {abs($value1-$value2)}
131} -result 0.001 ;# Zero is impossible
132
133test "BasicStats-1.11" "Basic statistics - missing data - pvar" -match almostzero -body {
134  set value1 [::math::statistics::pstdev  $::data_linear]
135  set value2 [::math::statistics::pvar    $::data_missing2]
136  expr {$value1*$value1-$value2}
137} -result 0.001 ;# Zero is impossible
138
139#
140# This test was added because the calculation of the standard deviation
141# could fail with uniform data (the difference of two almost equal
142# values became a small negative number)
143#
144# Further extension: more stable computation if the values are very
145# close together. Due to this change the variance should be independent
146# of the mean, however large (up to a point)
147#
148test "BasicStats-2.1" "Basic statistics - uniform data caused sqrt domain error" -body {
149  set values [list]
150  set count 0
151  for { set i 0 } { $i < 20 } { incr i } {
152     lappend values 0.6
153     set value2 [::math::statistics::mean $values]
154     incr count
155  }
156  set count
157} -result 20 ;# We can finish the loop
158
159test "BasicStats-2.2" "Basic statistics - large almost identical values" -match glob -body {
160  catch {
161     set data [list 100001 100002 100003 100004]
162     set result_large [::math::statistics::BasicStats all $data]
163
164     set data [list 1 2 3 4]
165     set result_small [::math::statistics::BasicStats all $data]
166
167     matchTolerant [lrange $result_small 3 end] [lrange $result_large 3 end]
168  } msg
169  set msg
170} -result 1
171
172#
173# Histograms
174#
175test "Histogram-1.0" "Histogram - uniform data" -match glob -body {
176  set values [::math::statistics::histogram {0 2} $::data_uniform]
177} -result [list 0 [llength $::data_uniform] 0]
178
179test "Histogram-1.1" "Histogram - missing data" -match glob -body {
180  set values [::math::statistics::histogram {0 2} $::data_missing]
181} -result [list 0 [::math::statistics::number $::data_missing] 0]
182
183test "Histogram-1.2" "Histogram - linear data" -match glob -body {
184  set values [::math::statistics::histogram {1.5 4.5 9.5} $::data_linear]
185} -result {1 3 5 1}
186
187test "Histogram-1.3" "Histogram - linear data 2" -match glob -body {
188  set values [::math::statistics::histogram {1.5 2.5 10.5} $::data_linear]
189} -result {1 1 8 0}
190
191#
192# Adding two dummy values should not influence the histogram (ticket 05d055c2f5)
193#
194test "Histogram-1.4" "Histogram - linear data 2 with weights" -match glob -body {
195  set values [::math::statistics::histogram {1.5 2.5 10.5} [concat $::data_linear 0.0 0.0] \
196      [concat [lrepeat [llength $::data_linear] 1] 0 0]]
197} -result {1 1 8 0}
198
199test "Histogram-1.5" "Histogram - linear data 2 with weights" -match glob -body {
200  set values [::math::statistics::histogram {1.5 2.5} [concat $::data_linear 0.0 0.0] \
201      [concat [lrepeat [llength $::data_linear] 1] 0 0]]
202} -result {1 1 8}
203
204#
205# Alternative definition of the intervals (ticket 1502400fff)
206# Note the difference in the expected bin sizes for the two
207#
208test "Histogram-2.1" "Histogram - alternative interval bounds" -match glob -body {
209  set values [concat [::math::statistics::histogram-alt {5.0 7.0} $::data_linear] \
210                     [::math::statistics::histogram     {5.0 7.0} $::data_linear]]
211} -result {4 2 4 5 2 3}
212
213#
214# Quantiles
215# Bug #1272910: related to rounding 0.5 - use different levels instead
216#               because another bug was fixed, return to the original
217#               levels again
218#
219test "Quantiles-1.0" "Quantiles - raw data" -match tolerant -body {
220  set values [::math::statistics::quantiles $::data_linear {0.25 0.55 0.95}]
221} -result {3.0 6.0 10.0}
222
223test "Quantiles-1.1" "Quantiles - histogram" -match tolerant -body {
224  set limits    {1.0 2.0 3.0 4.0}
225  set data_hist {0 10 20 10 0}
226  set values [::math::statistics::quantiles $limits $data_hist {0.25 0.5 0.9}]
227} -result {2.0 2.5 3.6}
228
229#
230# Generate histogram limits
231#
232
233test "Limits-1.0" "Limits - based on mean/stdev" -match tolerant -body {
234  set values [::math::statistics::mean-histogram-limits 1.0 1.0 4]
235} -result {0.0 0.75 1.25 2.0}
236
237test "Limits-1.1" "Limits - based on mean/stdev" -match tolerant -body {
238  set values [::math::statistics::mean-histogram-limits 1.0 1.0 9]
239} -result {-2.0 -1.0 0.0 0.75 1.0 1.25 2.0 3.0 4.0}
240
241test "Limits-1.2" "Limits - based on mean/stdev" -match tolerant -body {
242  set values [::math::statistics::mean-histogram-limits 0.0 1.0 11]
243} -result {-3.0 -2.4 -1.8 -1.2 -0.6 0.0 0.6 1.2 1.8 2.4 3.0}
244
245test "Limits-2.0" "Limits - based on min/max" -match tolerant -body {
246  set values [::math::statistics::minmax-histogram-limits -2.0 2.0 9]
247} -result {-2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0}
248
249test "Limits-2.1" "Limits - based on min/max" -match tolerant -body {
250  set values [::math::statistics::minmax-histogram-limits -2.0 2.0 2]
251} -result {-2.0 2.0}
252
253#
254# To do: design test cases for the following functions:
255# - t-test-mean
256# - estimate-mean-stdev
257# - autocorr
258# - crosscorr
259# - linear-model
260# - linear-residuals
261# - pdf-*
262# - cdf-*
263# - random-*
264# - histogram-*
265#
266# Crude test cases for Student's t test
267#
268test "Students-t-test-1.0" "Student's t - same sample" -match glob -body {
269  set sample [::math::statistics::random-normal 0.0 1.0 40]
270  set mean   0.0
271  set stdev  1.0
272  set confidence 0.95
273
274  set result [::math::statistics::t-test-mean $sample $mean $stdev $confidence]
275} -result 1
276
277test "Students-t-test-1.1" "Student's t - different sample" -match glob -body {
278  set sample [::math::statistics::random-normal 0.0 1.0 40]
279  set mean   10.0
280  set stdev   1.0
281  set confidence 0.95
282
283  set result [::math::statistics::t-test-mean $sample $mean $stdev $confidence]
284} -result 0
285
286test "Students-t-test-1.2" "Student's t - small sample" -match glob -body {
287  set sample [::math::statistics::random-normal 0.0 1.0 2]
288  set mean    2.0
289  set stdev   1.0
290  set confidence 0.90
291
292  set result [::math::statistics::t-test-mean $sample $mean $stdev $confidence]
293} -result 1
294
295#
296# Test private procedures
297#
298test "Cdf-toms322-1.0" "TOMS322 - erf(x)" -match tolerant2 -body {
299  set result {}
300  foreach z {4.417 3.891 3.291 2.576 2.241 1.960 1.645 1.150 0.674
301             0.319 0.126 0.063 0.0125} {
302     set prob [::math::statistics::Cdf-toms322 1 5000 [expr {$z*$z}]]
303     lappend result [expr {1.0-$prob}]
304  }
305  set result
306} -result {1.e-5 1.e-4 1.e-3 1.e-2 0.025 0.050 0.100 0.250 0.500
307           0.750 0.900 0.950 0.990 }
308
309test "Cdf-toms322-2.0" "TOMS322 - inverse erf(x)" -match tolerant2 -body {
310  set result {}
311  foreach p {0.5120 0.5948 0.7019 0.7996  0.8997  0.9505  0.9901  0.9980 } {
312     set z [::math::statistics::Inverse-cdf-normal 0.0 1.0 $p]
313     lappend result $z
314  }
315  set result
316} -result    {0.03  0.24   0.53   0.84    1.28    1.65    2.33    2.88 }
317
318#
319# Correlation coefficients
320#
321test "Correlation-1.0" "Correlation - linear data" -match tolerant -body {
322  set corr [::math::statistics::corr $::data_linear $::data_linear]
323} -result 1.0
324test "Correlation-1.1" "Correlation - linear/uniform" -match almostzero -body {
325  set corr [::math::statistics::corr $::data_linear $::data_uniform]
326} -result 0.0
327
328#
329# Test list procedures
330#
331proc matchListElements { expected actual } {
332   if { [llength $expected] != [llength $actual] } {
333      return 0
334   } else {
335      set match 1
336      foreach a $actual e $expected {
337         if { $a != $e } {
338            set match 0
339            break
340         }
341      }
342   }
343   return $match
344}
345customMatch matchList  matchListElements
346
347set ::data_list {1 2 3 4 5 6 7 8 9 10}
348set ::data_pairs {{1 2} {3 4} {5 6} {7 8} {9 10}}
349
350test "Filter-1.0" "True filter" -match matchList -body {
351   set data [::math::statistics::filter x $::data_list 1]
352} -result $::data_list
353
354test "Filter-1.1" "False filter" -match matchList -body {
355   set data [::math::statistics::filter x $::data_list 0]
356} -result {}
357
358test "Filter-1.2" "Even filter" -match matchList -body {
359   set data [::math::statistics::filter x $::data_list {$x%2==0}]
360} -result {2 4 6 8 10}
361
362test "Filter-2.1" "filter with parameter" -match matchList -body {
363   set param 3.0
364   set data [::math::statistics::filter x $::data_list {$x > $param}]
365} -result {4 5 6 7 8 9 10}
366
367test "Map-1.0" "Identity map" -match matchList -body {
368   set data [::math::statistics::map x $::data_list {$x}]
369} -result $::data_list
370
371test "Map-1.1" "Is-even map" -match matchList -body {
372   set data [::math::statistics::map x $::data_list {$x%2==0}]
373} -result {0 1 0 1 0 1 0 1 0 1}
374
375test "Map-1.2" "Double map" -match matchList -body {
376   set data [::math::statistics::map x $::data_list {$x*2}]
377} -result {2 4 6 8 10 12 14 16 18 20}
378
379test "Map-2.1" "map with parameter" -match matchList -body {
380   set param 3.0
381   set data [::math::statistics::map x $::data_list {$x + $param}]
382} -result {4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0}
383
384test "Samplescount-1.0" "Single sublist" -match matchList -body {
385   set data [::math::statistics::samplescount x [list $::data_list]]
386} -result {10}
387
388test "Samplescount-1.1" "List of singleton sublist" -match matchList -body {
389   set data [::math::statistics::samplescount x $::data_list]
390} -result {1 1 1 1 1 1 1 1 1 1}
391
392test "Samplescount-1.2" "Pairs sublist" -match matchList -body {
393   set data [::math::statistics::samplescount x $::data_pairs]
394} -result {2 2 2 2 2}
395
396test "Samplescount-1.3" "Select uneven sublist" -match matchList -body {
397   set data [::math::statistics::samplescount x $::data_pairs {$x%2}]
398} -result {1 1 1 1 1}
399
400test "Samplescount-2.1" "Count with parameter" -match matchList -body {
401   set param 3.0
402   set data [::math::statistics::samplescount x $::data_pairs {$x>$param}]
403} -result {0 1 2 2 2}
404
405test "Median-1.1" "Median - odd number of data" -body {
406   set data {1.0 3.0 2.0}
407   set median [::math::statistics::median $data]
408} -result 2.0
409
410test "Median-1.2" "Median - even number of data" -body {
411   set data {1.0 3.0 2.0 1.0}
412   set median [::math::statistics::median $data]
413} -result 1.5
414
415test "Median-1.3" "Median - missing data" -body {
416   set data {1.0 {} 3.0 2.0 1.0 {}}
417   set median [::math::statistics::median $data]
418} -result 1.5
419
420test "test-2x2-1.0" "Test 2x2" -match tolerant -body {
421   set data [::math::statistics::test-2x2 170 94 30 6]
422} -result 5.1136364
423
424test "test-xbar-1.0" "Test xbar procedure" -match exact -body {
425    set data {}
426    for { set i 0 } { $i < 500 } { incr i } {
427        lappend data [expr {rand()}]
428    }
429    set limits  [::math::statistics::control-xbar $data]
430    set newdata {1.0 1.0 1.0 1.0 0.5 0.5 0.5 0.5 10.0 10.0 10.0 10.0}
431    set result  [::math::statistics::test-xbar $limits $newdata]
432} -result {0 2}
433
434test "test-Rchart-1.0" "Test Rchart procedure" -match exact -body {
435    set data {}
436    for { set i 0 } { $i < 500 } { incr i } {
437        lappend data [expr {rand()}]
438    }
439    set limits  [::math::statistics::control-Rchart $data]
440    set newdata {0.0 1.0 2.0 1.0 0.4 0.5 0.6 0.5 10.0  0.0 10.0 10.0}
441    set result  [::math::statistics::test-Rchart $limits $newdata]
442} -result {0 2}
443
444#
445# Testing for normal distribution
446#
447test "Testnormal-1.0" "Determine normality statistic for birth weight data" -match tolerant -body {
448    ::math::statistics::lillieforsFit {72 112 111 107 119  92 126  80 81 84 115
449                                       118 128 128 123 116 125 126 122 126 127 86
450                                       142 132  87 123 133 106 103 118 114 94}
451} -result 0.82827415657
452
453test "Testnormal-1.0" "Test birthweight data for normality - 20% significance" -match exact -body {
454    ::math::statistics::test-normal {72 112 111 107 119  92 126  80 81 84 115
455                                     118 128 128 123 116 125 126 122 126 127 86
456                                     142 132  87 123 133 106 103 118 114 94} 0.20
457} -result 1
458
459test "Testnormal-1.0" "Test birthweight data for normality - 5% significance" -match exact -body {
460    ::math::statistics::test-normal {72 112 111 107 119  92 126  80 81 84 115
461                                     118 128 128 123 116 125 126 122 126 127 86
462                                     142 132  87 123 133 106 103 118 114 94} 0.05
463} -result 0
464
465test "Test-Duckworth-1.0" "Test Tukey-Duckworth - 5% significance" -match exact -body {
466    set list1 {10 2 3 4 6}
467    set list2 {12 3 4 6}
468
469    ::math::statistics::test-Duckworth $list1 $list2 0.05
470} -result 1
471
472test "Test-Duckworth-1.1" "Test Tukey-Duckworth - symmetry" -match exact -body {
473    set list1 {1 2 3 4 5 6 7 8 9 10}
474    set list2 {6 7 8 9 10 11 12 13 14 15 16 17}
475
476    set result [list [::math::statistics::test-Duckworth $list1 $list2 0.05] \
477                     [::math::statistics::test-Duckworth $list2 $list1 0.05]]
478} -result {0 0}
479
480test "Test-Duckworth-1.2" "Test Tukey-Duckworth - applicability" -match exact -body {
481    set list1 {2 3 4 6 20}
482    set list2 {12 3 4 6}
483
484    ::math::statistics::test-Duckworth $list1 $list2 0.05
485} -result -1
486
487#
488# Testing multivariate linear regression
489#
490# Provide some data
491test "Testmultivar-1.0" "Ordinary multivariate regression - three independent variables" \
492        -match tolerant -body {
493    set data {
494        {  -.67  14.18  60.03  -7.5}
495        { 36.97  15.52  34.24  14.61}
496        {-29.57  21.85  83.36  -7.}
497        {-16.9   11.79  51.67  -6.56}
498        { 14.09  16.24  36.97  -12.84}
499        { 31.52  20.93  45.99  -25.4}
500        { 24.05  20.69  50.27  17.27}
501        { 22.23  16.91  45.07  -4.3}
502        { 40.79  20.49  38.92  -.73}
503        {-10.35  17.24  58.77 18.78}}
504
505    # Call the ols routine
506    set results [::math::statistics::mv-ols $data]
507
508    # Flatten the result (so that we can use the tolerant comparison method)
509    eval concat [eval concat $results]
510} -result {0.887239767929 0.830859651893
5113.33854942057 -1.58346976987 0.0362328113288 32.571621244
5121.03305463908 0.237943867401 0.234143883673 19.4700016828
5130.810755783819 5.86634305732
514-2.16569743834 -1.00124210139 -0.536696631937 0.609162254594
515-15.0697565684 80.2129990564}
516
517
518test "Testmultivar-1.1" "Ordinary multivariate regression - zero variation" -body {
519    set results [::math::statistics::mv-ols {{0 25125 128} {0 23224 64} {0 37903 512} {0 21263 32}
520                                             {0 22053 64} {0 25745 256} {0 25745 256} {0 21557 32}
521                                             {0 24935 128} {0 22904 64} {0 21422 32} {0 21947 32}
522                                             {0 33244 512} {0 33244 512} {0 30060 512} {0 29691 256}
523                                             {0 30439 256} {0 23724 128} {0 22541 64} {0 23640 128}
524                                             {0 21422 32} {0 23640 128} {0 22249 64} {0 28247 512}
525                                             {0 23333 32} {0 29841 256} {0 23959 128} {0 30819 512}
526                                             {0 26333 256} {0 22145 32} {0 23863 128} {0 20772 32}
527                                             {0 28511 512} {0 22425 64} {0 21598 32} {0 26335 256}
528                                             {0 23816 128} {0 21157 32} {0 20973 32} {0 20973 32}
529                                             {0 35125 512} {0 20679 32} {0 21241 64} {0 25297 256}
530                                             {0 22301 32} {0 22007 32} {0 33351 512} {0 24115 128}
531                                             {0 24115 128} {0 22301 32} {0 22797 64} {0 22593 64}
532                                             {0 26439 256} {0 21255 32} {0 22645 32} {0 23447 128}
533                                             {0 24205 64} {0 25051 128} {0 21007 32} {0 28237 256}
534                                             {0 25546 128} {0 25669 256} {0 25669 256} {0 25669 256}
535                                             {0 21977 64} {0 21977 64} {0 26187 128} {0 38360 512}
536                                             {0 31846 256} {0 28349 256} {0 26450 128}}]
537    set r2 [lindex $results 0]
538} -result 1.0
539
540#
541# pdf/cdf tests - transformed from the contributions by Eric K. Benedict
542#                 Cf. the examples.
543#
544# Note: cases with integer numbers test if divisions are done in floating-point or not
545#
546
547test "uniform-distribution-1.0" "Test pdf-uniform" -match tolerant -body {
548    set x [list \
549        [::math::statistics::pdf-uniform   0   10  5] \
550        [::math::statistics::pdf-uniform   0.0 1.0 0.5] \
551        [::math::statistics::pdf-uniform -10.0 1.0 -4.5] \
552        [::math::statistics::pdf-uniform  -2.0 2.0  1.0]]
553} -result {0.1 1.0 0.0909090909 0.25}
554
555test "uniform-distribution-1.1" "Test cdf-uniform" -match tolerant -body {
556    set x [list \
557        [::math::statistics::cdf-uniform   0   10  5] \
558        [::math::statistics::cdf-uniform   0.0 1.0 0.5] \
559        [::math::statistics::cdf-uniform -10.0 1.0 -4.5] \
560        [::math::statistics::cdf-uniform  -2.0 2.0  1.0]]
561} -result {0.5 0.5 0.5 0.75}
562
563test "triangular-distribution-1.0" "Test pdf-triangular" -match tolerant -body {
564    set x [list \
565        [::math::statistics::pdf-triangular   0   1.0 0.5]  \
566        [::math::statistics::pdf-triangular   1.0 0.0 0.5]  \
567        [::math::statistics::pdf-triangular   0.0 1.0 0.25] \
568        [::math::statistics::pdf-triangular   1.0 0.0 0.25] \
569        [::math::statistics::pdf-triangular   0.0 2.0 0.0]  \
570        [::math::statistics::pdf-triangular   0.0 2.0 1.0]  \
571        [::math::statistics::pdf-triangular   0.0 2.0 2.0]]
572} -result {1.0 1.0 1.5 0.5 2.0 1.0 0.0}
573test "triangular-distribution-1.1" "Test cdf-triangular" -match tolerant -body {
574    set x [list \
575        [::math::statistics::cdf-triangular   0   1.0  0.5] \
576        [::math::statistics::cdf-triangular   1.0 0.0  0.5] \
577        [::math::statistics::cdf-triangular   0.0 1.0  0.25] \
578        [::math::statistics::cdf-triangular   1.0 0.0  0.25] \
579        [::math::statistics::cdf-triangular   0.0 2.0  0.0] \
580        [::math::statistics::cdf-triangular   0.0 2.0  1.0] \
581        [::math::statistics::cdf-triangular   0.0 2.0  2.0]]
582} -result {0.75 0.25 0.4375 0.0625 0.0 0.75 1.0}
583
584test "triangular-symmetric-distribution-1.0" "Test pdf-symmetric-triangular" -match tolerant -body {
585    set x [list \
586        [::math::statistics::pdf-symmetric-triangular 0.0 2.0 -0.5]  \
587        [::math::statistics::pdf-symmetric-triangular 0.0 2.0  0.0]  \
588        [::math::statistics::pdf-symmetric-triangular 0.0 2.0  0.5]  \
589        [::math::statistics::pdf-symmetric-triangular 0.0 2.0  1.0]  \
590        [::math::statistics::pdf-symmetric-triangular 0.0 2.0  1.5]  \
591        [::math::statistics::pdf-symmetric-triangular 0.0 2.0  2.0]  \
592        [::math::statistics::pdf-symmetric-triangular 0.0 2.0  2.5]]
593} -result {0.0 0.0 0.5 1.0 0.5 0.0 0.0}
594
595test "triangular-symmetric-distribution-1.1" "Test cdf-symmetric-triangular" -match tolerant -body {
596    set x [list \
597        [::math::statistics::cdf-symmetric-triangular 0.0 2.0 -0.5]  \
598        [::math::statistics::cdf-symmetric-triangular 0.0 2.0  0.0]  \
599        [::math::statistics::cdf-symmetric-triangular 0.0 2.0  0.5]  \
600        [::math::statistics::cdf-symmetric-triangular 0.0 2.0  1.0]  \
601        [::math::statistics::cdf-symmetric-triangular 0.0 2.0  1.5]  \
602        [::math::statistics::cdf-symmetric-triangular 0.0 2.0  2.0]  \
603        [::math::statistics::cdf-symmetric-triangular 0.0 2.0  2.5]]
604} -result {0.0 0.0 0.125 0.5 0.875 1.0 1.0}
605
606test "exponential-distribution-1.0" "Test pdf-exponential" -match tolerant -body {
607    set x [list \
608        [::math::statistics::pdf-exponential 2   1] \
609        [::math::statistics::pdf-exponential 1.0 1.0] \
610        [::math::statistics::pdf-exponential 2.0 2.0] \
611        [::math::statistics::pdf-exponential 2.0 1.0]]
612} -result {0.3032653298563167 0.36787944117144233 0.18393972058572117 0.3032653298563167}
613
614test "exponential-distribution-1.1" "Test cdf-exponential" -match tolerant -body {
615    set x [list \
616        [::math::statistics::cdf-exponential 2   1] \
617        [::math::statistics::cdf-exponential 1.0 1.0] \
618        [::math::statistics::cdf-exponential 2.0 2.0] \
619        [::math::statistics::cdf-exponential 2.0 1.0]]
620} -result {0.3934693402873666 0.6321205588285577 0.6321205588285577 0.3934693402873666}
621
622test "laplace-distribution-1.0" "Test pdf-laplace" -match tolerant -body {
623    set x [list \
624        [::math::statistics::pdf-laplace  1   1   1] \
625        [::math::statistics::pdf-laplace  1.0 1.0 2] \
626        [::math::statistics::pdf-laplace -1.0 2.0 0] \
627        [::math::statistics::pdf-laplace  1.5 2.5 3]]
628} -result {0.5 0.18393972058572117 0.15163266492815836 0.1097623272188053}
629
630test "laplace-distribution-1.1" "Test cdf-laplace" -match tolerant -body {
631    set x [list \
632        [::math::statistics::cdf-laplace  1   1   1] \
633        [::math::statistics::cdf-laplace  1.0 1.0 2] \
634        [::math::statistics::cdf-laplace -1.0 2.0 0] \
635        [::math::statistics::cdf-laplace  1.5 2.5 3]]
636} -result {0.5 0.8160602794142788 0.6967346701436833 0.7255941819529867}
637
638test "kumaraswamy-distribution-1.0" "Test pdf-kumaraswamy" -match tolerant -body {
639    set x [list \
640        [::math::statistics::pdf-kumaraswamy  1   1   0.5] \
641        [::math::statistics::pdf-kumaraswamy  0.5 0.5 0.5] \
642        [::math::statistics::pdf-kumaraswamy  1.0 2.0 0] \
643        [::math::statistics::pdf-kumaraswamy  1.0 2.0 0.5]]
644} -result {1.0 0.6532814824381884 2.0 1.0}
645
646test "kumaraswamy-distribution-1.1" "Test cdf-kumaraswamy" -match tolerant -body {
647    set x [list \
648        [::math::statistics::cdf-kumaraswamy  1   1   0.5] \
649        [::math::statistics::cdf-kumaraswamy  0.5 0.5 0.5] \
650        [::math::statistics::cdf-kumaraswamy  1.0 2.0 0] \
651        [::math::statistics::cdf-kumaraswamy  1.0 2.0 0.5]]
652} -result {0.5 0.4588038998538031 0.0 0.75}
653
654# Non-trivial examples from Wikipedia
655test "negative-binomial-distribution-1.0" "Test pdf-negative-binomial" -match tolerant -body {
656    set x [list \
657        [::math::statistics::pdf-negative-binomial 1 0.3 0] \
658        [::math::statistics::pdf-negative-binomial 1 0.9 0] \
659        [::math::statistics::pdf-negative-binomial 5 0.4 0] \
660        [::math::statistics::pdf-negative-binomial 5 0.4 1] \
661        [::math::statistics::pdf-negative-binomial 5 0.4 2] \
662        [::math::statistics::pdf-negative-binomial 5 0.4 3] \
663        [::math::statistics::pdf-negative-binomial 5 0.4 5]]
664} -result {0.7 0.1 0.07776 0.15552 0.186624 0.1741824 0.10032906}
665
666test "negative-binomial-distribution-1.1" "Test cdf-negative-binomial" -match tolerant -body {
667    set x [list \
668        [::math::statistics::cdf-negative-binomial 5 0.4 3] \
669        [::math::statistics::cdf-negative-binomial 1 0.9 0] \
670        [::math::statistics::cdf-negative-binomial 1 0.9 1] \
671        [::math::statistics::cdf-negative-binomial 1 0.9 2]]
672} -result {0.59408 0.1 0.19 0.271}
673
674test "normal-distribution-1.0" "Test pdf-normal" -match tolerant -body {
675    set x [list \
676        [::math::statistics::pdf-normal  0   1   1] \
677        [::math::statistics::pdf-normal  0.0 1.0 1.0] \
678        [::math::statistics::pdf-normal  2.0 2.0 4.0] \
679        [::math::statistics::pdf-normal -2.0 2.0 0.0] \
680        [::math::statistics::pdf-normal  2.0 2.0 3.0]]
681} -result {0.24197072451914337 0.24197072451914337 0.12098536225957168 0.12098536225957168 0.17603266338214976}
682
683test "normal-distribution-1.1" "Test cdf-normal" -match tolerant -body {
684    set x [list \
685        [::math::statistics::cdf-normal  0   1   1] \
686        [::math::statistics::cdf-normal  0.0 1.0 1.0] \
687        [::math::statistics::cdf-normal  2.0 2.0 4.0] \
688        [::math::statistics::cdf-normal -2.0 2.0 0.0] \
689        [::math::statistics::cdf-normal  2.0 2.0 3.0]]
690} -result {0.8413205502059895 0.8413205502059895 0.8413205502059895 0.8413205502059895 0.691451459572962}
691
692#
693# The values of the mean and the standard deviation are reconstructed from mu and sigma.
694# If you use mu and sigma in pdf-normal/cdf-normal, the same numbers should arise
695#
696test "lognormal-distribution-1.0" "Test pdf-lognormal" -match tolerant -body {
697    foreach {mu sigma mean stdev} {0.0 1.0 mean1 stdev1 2.0 2.0 mean2 stdev2 -2.0 2.0 mean3 stdev3} {
698        set m      [expr {exp($mu + $sigma*$sigma/2.0)}]
699        set $mean  $m
700        set $stdev [expr {sqrt(exp($sigma*$sigma) - 1.0) * $m}]
701    }
702
703    set x [list \
704        [::math::statistics::pdf-lognormal  $mean1 $stdev1 [expr {exp(1.0)}]] \
705        [::math::statistics::pdf-lognormal  $mean2 $stdev2 [expr {exp(4.0)}]] \
706        [::math::statistics::pdf-lognormal  $mean3 $stdev3 [expr {exp(0.0)}]] \
707        [::math::statistics::pdf-lognormal  $mean2 $stdev2 [expr {exp(3.0)}]]]
708} -result {0.24197072451914337 0.12098536225957168 0.12098536225957168 0.17603266338214976}
709
710test "lognormal-distribution-1.1" "Test cdf-lognormal" -match tolerant -body {
711    foreach {mu sigma mean stdev} {0.0 1.0 mean1 stdev1 2.0 2.0 mean2 stdev2 -2.0 2.0 mean3 stdev3} {
712        set m      [expr {exp($mu + $sigma*$sigma/2.0)}]
713        set $mean  $m
714        set $stdev [expr {sqrt(exp($sigma*$sigma) - 1.0) * $m}]
715    }
716
717    set x [list \
718        [::math::statistics::cdf-lognormal  $mean1 $stdev1 [expr {exp(1.0)}]] \
719        [::math::statistics::cdf-lognormal  $mean2 $stdev2 [expr {exp(4.0)}]] \
720        [::math::statistics::cdf-lognormal  $mean3 $stdev3 [expr {exp(0.0)}]] \
721        [::math::statistics::cdf-lognormal  $mean2 $stdev2 [expr {exp(3.0)}]]]
722} -result {0.8413205502059895 0.8413205502059895 0.8413205502059895 0.691451459572962}
723
724test "gamma-distribution-1.0" "Test pdf-gamma" -match tolerant -body {
725    set x [list \
726        [::math::statistics::pdf-gamma 1.5 2.7 3.0] \
727        [::math::statistics::pdf-gamma 7.5 0.2 30.0] \
728        [::math::statistics::pdf-gamma 15.0 1.2 2.0]]
729} -result {0.00263194027271168 0.0302770403110644 2.62677891379834e-07}
730
731test "gamma-distribution-1.1" "Test cdf-gamma" -match tolerant -body {
732    set x [list \
733        [::math::statistics::cdf-gamma 1.9 0.45 2.5] \
734        [::math::statistics::cdf-gamma 45.0 2.2 32.7]]
735} -result {0.340299345090375 0.999731419881902}
736
737test "poisson-distribution-1.0" "Test pdf-poisson" -match tolerant -body {
738    set x [list \
739        [::math::statistics::pdf-poisson 100 130] \
740        [::math::statistics::pdf-poisson 27.2 37] \
741        [::math::statistics::pdf-poisson 7.3 11.2]]
742} -result {0.000575252683815462 0.0134122817590761 0.0530940708960824}
743
744test "poisson-distribution-1.1" "Test cdf-poisson" -match tolerant -body {
745    set x [list \
746        [::math::statistics::cdf-poisson 4 7] \
747        [::math::statistics::cdf-poisson 80 70] \
748        [::math::statistics::cdf-poisson 4.9 6.2]]
749} -result {0.948866384207153 0.14338996716003 0.77665467292263}
750
751test "chisquare-distribution-1.0" "Test pdf-chisquare" -match tolerant -body {
752    set x [list \
753        [::math::statistics::pdf-chisquare 3 1.75]  \
754        [::math::statistics::pdf-chisquare 10 2.9]  \
755        [::math::statistics::pdf-chisquare 4 17.45] \
756        [::math::statistics::pdf-chisquare 2.5 1.8]]
757} -result {0.219999360547348 0.0216024880121444 0.000708787557977144 0.218446210041615}
758
759test "chisquare-distribution-1.1" "Test cdf-chisquare" -match tolerant -body {
760    set x [list \
761        [::math::statistics::cdf-chisquare 2 3.5]   \
762        [::math::statistics::cdf-chisquare 5 2.2]   \
763        [::math::statistics::cdf-chisquare 5 100]   \
764        [::math::statistics::cdf-chisquare 3.9 4.2] \
765        [::math::statistics::cdf-chisquare 1  2.0]  \
766        [::math::statistics::cdf-chisquare 3 -2.0]]
767} -result {0.826226056549555 0.179164030785504 1.0 0.634682741547709 0.842700792949715 0.0}
768
769test "students-t-distribution-1.0" "Test pdf-students-t" -match tolerant -body {
770    set x [list \
771        [::math::statistics::pdf-students-t 1 0.1]  \
772        [::math::statistics::pdf-students-t 0.5 0.1]  \
773        [::math::statistics::pdf-students-t 4 3.2]  \
774        [::math::statistics::pdf-students-t 3 2.0]  \
775        [::math::statistics::pdf-students-t 3 7.5]]
776} -result {0.315158303152268 0.265700672177405 0.0156821741652879 0.0675096606638929 0.000942291548015668}
777
778test "beta-distribution-1.0" "Test pdf-beta" -match tolerant -body {
779    set x [list \
780        [::math::statistics::pdf-beta 1.3 2.4 0.2] \
781        [::math::statistics::pdf-beta 1 1 0.5] \
782        [::math::statistics::pdf-beta 3.7 0.9 0.0] \
783        [::math::statistics::pdf-beta 1.8 4.2 1.0] \
784        [::math::statistics::pdf-beta 320 400 0.4] \
785        [::math::statistics::pdf-beta 500   1 0.2] \
786        [::math::statistics::pdf-beta 1000 1000 0.50]]
787} -result {1.68903180472449 1.0 0.0 0.0 1.18192376783860 0.0 35.6780222917086}
788
789test "beta-distribution-1.1" "Test cdf-beta" -match tolerant -body {
790    set x [list \
791        [::math::statistics::cdf-beta 2.1 3.0 0.2] \
792        [::math::statistics::cdf-beta 4.2 17.3 0.5] \
793        [::math::statistics::cdf-beta 500 375 0.7] \
794        [::math::statistics::cdf-beta 250 760 0.2] \
795        [::math::statistics::cdf-beta 43.2 19.7 0.6] \
796        [::math::statistics::cdf-beta 500 640 0.3] \
797        [::math::statistics::cdf-beta 400 640 0.3] \
798        [::math::statistics::cdf-beta 0.1 30 0.1] \
799        [::math::statistics::cdf-beta 0.01 0.03 0.9] \
800        [::math::statistics::cdf-beta 2 3 0.9999] \
801        [::math::statistics::cdf-beta 249.9999 759.99999 0.2] \
802        [::math::statistics::cdf-beta 1000 1000 0.4] \
803        [::math::statistics::cdf-beta 1000 1000 0.499] \
804        [::math::statistics::cdf-beta 1000 1000 0.5] \
805        [::math::statistics::cdf-beta 1000 1000 0.7] \
806        [::math::statistics::cdf-beta 2 3 0.6]]
807} -result {0.16220409275804 0.998630771123192 1.0 0.000125234318666948 0.0728881294218269
808           2.99872547567313e-23 3.07056696205524e-09 0.998641008671625 0.765865005703006
809           0.999999999996 0.000125237075575121 8.23161135486914e-20 0.464369443974288
810           0.5 1.0 0.8208}
811
812#
813# TODO: chose the tests with _integer_ arguments more carefully
814#
815test "gumbel-distribution-1.0" "Test pdf-gumbel" -match tolerant -body {
816    set x [list \
817        [::math::statistics::pdf-gumbel 1.0 1.0 0.0] \
818        [::math::statistics::pdf-gumbel 1.0 1.0 0.1] \
819        [::math::statistics::pdf-gumbel 1.0 1.0 0.2] \
820        [::math::statistics::pdf-gumbel 1.0 1.0 1.0] \
821        [::math::statistics::pdf-gumbel 1.0 1.0 2.0] \
822        [::math::statistics::pdf-gumbel 1.0 1.0 5.0] \
823        [::math::statistics::pdf-gumbel 0.1 2.0 0.0] \
824        [::math::statistics::pdf-gumbel 0.1 2.0 1.0] \
825        [::math::statistics::pdf-gumbel 0.1 2.0 2.0] \
826        [::math::statistics::pdf-gumbel 0.1 2.0 5.0] \
827        [::math::statistics::pdf-gumbel 1   1   5  ] ]
828} -result {0.179374 0.210219 0.240378 0.367879 0.254646 0.017983 0.183706 0.168507 0.131350 0.039580 0.017983}
829
830test "gumbel-distribution-1.1" "Test cdf-gumbel" -match tolerant -body {
831    set x [list \
832        [::math::statistics::cdf-gumbel 1.0 1.0 0.0] \
833        [::math::statistics::cdf-gumbel 1.0 1.0 0.2] \
834        [::math::statistics::cdf-gumbel 1.0 1.0 1.0] \
835        [::math::statistics::cdf-gumbel 1.0 1.0 2.0] \
836        [::math::statistics::cdf-gumbel 0.1 2.0 0.0] \
837        [::math::statistics::cdf-gumbel 0.1 2.0 1.0] \
838        [::math::statistics::cdf-gumbel 0.1 2.0 2.0] \
839        [::math::statistics::cdf-gumbel 1   1   2  ] ]
840} -result {0.065988 0.108009 0.367879 0.692201 0.349493 0.528544 0.679266 0.692201}
841
842test "weibull-distribution-1.0" "Test pdf-weibull" -match tolerant -body {
843    set x [list \
844        [::math::statistics::pdf-weibull 1.0 1.0 -1.0] \
845        [::math::statistics::pdf-weibull 1.0 1.0 0.0] \
846        [::math::statistics::pdf-weibull 1.0 1.0 0.1] \
847        [::math::statistics::pdf-weibull 1.0 1.0 0.2] \
848        [::math::statistics::pdf-weibull 1.0 1.0 1.0] \
849        [::math::statistics::pdf-weibull 1.0 1.0 2.0] \
850        [::math::statistics::pdf-weibull 1.0 1.0 5.0] \
851        [::math::statistics::pdf-weibull 2.0 2.0 0.0] \
852        [::math::statistics::pdf-weibull 2.0 2.0 1.0] \
853        [::math::statistics::pdf-weibull 2.0 2.0 2.0] \
854        [::math::statistics::pdf-weibull 2.0 2.0 5.0] ]
855} -result {0 1.0 0.904837 0.818730 0.367879 0.135335 0.006738 0 0.389400 0.367879 0.004826}
856
857test "weibull-distribution-1.1" "Test cdf-weibull" -match tolerant -body {
858    set x [list \
859        [::math::statistics::cdf-weibull 1.0 1.0 -1.0] \
860        [::math::statistics::cdf-weibull 1.0 1.0 0.0] \
861        [::math::statistics::cdf-weibull 1.0 1.0 0.2] \
862        [::math::statistics::cdf-weibull 1.0 1.0 1.0] \
863        [::math::statistics::cdf-weibull 1.0 1.0 2.0] \
864        [::math::statistics::cdf-weibull 2.0 2.0 0.0] \
865        [::math::statistics::cdf-weibull 2.0 2.0 1.0] \
866        [::math::statistics::cdf-weibull 2.0 2.0 2.0] \
867        [::math::statistics::cdf-weibull 2   2   2  ] ]
868} -result {0 0 0.181269 0.632106 0.864665 0 0.221199 0.632121 0.632121}
869
870test "pareto-distribution-1.0" "Test pdf-pareto" -match tolerant -body {
871    set x [list \
872        [::math::statistics::pdf-pareto 1.0 1.0 0.0] \
873        [::math::statistics::pdf-pareto 1.0 1.0 1.1] \
874        [::math::statistics::pdf-pareto 1.0 1.0 1.2] \
875        [::math::statistics::pdf-pareto 1.0 1.0 2.0] \
876        [::math::statistics::pdf-pareto 1.0 1.0 3.0] \
877        [::math::statistics::pdf-pareto 1.0 1.0 5.0] \
878        [::math::statistics::pdf-pareto 2.0 2.0 2.1] \
879        [::math::statistics::pdf-pareto 2.0 2.0 3.0] \
880        [::math::statistics::pdf-pareto 2.0 2.0 5.0] \
881        [::math::statistics::pdf-pareto 2.0 2.0 10.0] ]
882} -result {0 0.826446 0.694444 0.25 0.111111 0.04 0.863838 0.296296 0.064 0.008}
883
884test "pareto-distribution-1.1" "Test cdf-pareto" -match tolerant -body {
885    set x [list \
886        [::math::statistics::cdf-pareto 1.0 1.0 0.0] \
887        [::math::statistics::cdf-pareto 1.0 1.0 1.1] \
888        [::math::statistics::cdf-pareto 1.0 1.0 1.2] \
889        [::math::statistics::cdf-pareto 1.0 1.0 2.0] \
890        [::math::statistics::cdf-pareto 1.0 1.0 3.0] \
891        [::math::statistics::cdf-pareto 2.0 2.0 2.1] \
892        [::math::statistics::cdf-pareto 2.0 2.0 3.0] \
893        [::math::statistics::cdf-pareto 2.0 2.0 5.0] \
894        [::math::statistics::cdf-pareto 2   2   3  ] ]
895} -result {0 0.090909 0.1666667 0.5 0.666667 0.092971 0.555556 0.84 0.555556}
896
897test "cauchy-distribution-1.0" "Test pdf-cauchy" -match tolerant -body {
898    set x [list \
899        [::math::statistics::pdf-cauchy 1.0 1.0 0.0] \
900        [::math::statistics::pdf-cauchy 2.0 1.0 1.0] \
901        [::math::statistics::pdf-cauchy 1.0 2.0 2.0] \
902        [::math::statistics::pdf-cauchy 2.0 2.0 2.0] ]
903} -result {0.1591555 0.1591555 0.1273240 0.1591550}
904
905test "cauchy-distribution-1.1" "Test cdf-cauchy" -match tolerant -body {
906    set x [list \
907        [::math::statistics::cdf-cauchy 1.0 1.0 0.0] \
908        [::math::statistics::cdf-cauchy 2.0 1.0 1.0] \
909        [::math::statistics::cdf-cauchy 1.0 2.0 2.0] \
910        [::math::statistics::cdf-cauchy 2.0 2.0 2.0] ]
911} -result {0.25 0.25 0.6475836 0.5}
912
913test "F-distribution-1.1" "Test cdf-F" -match tolerant -body {
914    # Note: returned values are 1-0.05, 1-0.10 and 1-0.01
915    set x [list \
916        [::math::statistics::cdf-F  3  3 9.277] \
917        [::math::statistics::cdf-F  1 30 4.171] \
918        [::math::statistics::cdf-F  7  8 3.500] \
919        [::math::statistics::cdf-F  2 10 2.924] \
920        [::math::statistics::cdf-F 17  2 9.433] \
921        [::math::statistics::cdf-F  5  6 8.746] \
922        [::math::statistics::cdf-F  8 11 4.744] ]
923} -result {0.95 0.95 0.95 0.90 0.90 0.99 0.99}
924
925test "empirical-distribution-1.0" "Test empirical-distribution" -match tolerant -body {
926    set x {10 4 3 2 5 6 7}
927    set distribution [::math::statistics::empirical-distribution $x]
928} -result {2 0.086207 3 0.224138 4 0.36207 5 0.5 6 0.637910 7 0.775862 10 0.913793}
929
930#
931# Crude tests for the random number generators
932# Mainly to verify that there are no obvious errors
933#
934# To verify that the values are scaled properly, use a fixed seed
935#
936set ::rseed 1000000
937
938test "random-numbers-1.0" "Test random-uniform" -body {
939    expr {srand($::rseed)}
940
941    set rnumbers [::math::statistics::random-uniform 0 10 100]
942
943    set inrange 1
944    foreach r $rnumbers {
945        if { $r < 0.0 || $r > 10.0 } {
946            set inrange 0
947            break
948        }
949    }
950
951    expr {srand($::rseed)}
952    set scaled    1
953    set rnumbers2 [::math::statistics::random-uniform 0 20 100]
954    foreach r1 $rnumbers r2 $rnumbers2 {
955        set scale [expr {$r2 / $r1}]
956        if { abs($scale - 2.0) > 0.00001 } {
957            set scaled 0
958        }
959    }
960    expr {srand($::rseed)}
961    set shifted   1
962    set rnumbers3 [::math::statistics::random-uniform 10 20 100]
963    foreach r1 $rnumbers r3 $rnumbers3 {
964        set shift [expr {$r3 - $r1}]
965        if { abs($shift - 10.0) > 0.00001 } {
966            set shifted 0
967        }
968    }
969
970    set result [list $inrange [llength $rnumbers] $scaled $shifted]
971} -result {1 100 1 1}
972
973test "random-numbers-1.1" "Test random-exponential" -body {
974    expr {srand($::rseed)}
975    set rnumbers [::math::statistics::random-exponential 1 100]
976
977    set inrange 1
978    foreach r $rnumbers {
979        if { $r < 0.0 } {
980            set inrange 0
981            break
982        }
983    }
984
985    expr {srand($::rseed)}
986    set scaled    1
987    set rnumbers2 [::math::statistics::random-exponential 2 100]
988    foreach r1 $rnumbers r2 $rnumbers2 {
989        set scale [expr {$r2 / $r1}]
990        if { abs($scale - 2.0) > 0.00001 } {
991            set scaled 0
992        }
993    }
994    set result [list $inrange [llength $rnumbers] $scaled]
995} -result {1 100 1}
996
997test "random-numbers-1.2" "Test random-normal" -body {
998    set rnumbers [::math::statistics::random-normal 0 1 100]
999    set result [llength $rnumbers]
1000} -result 100
1001
1002test "random-numbers-1.3" "Test random-gamma" -body {
1003    set rnumbers [::math::statistics::random-gamma 1.5 2.7 100]
1004    set result [llength $rnumbers]
1005} -result 100
1006
1007test "random-numbers-1.4" "Test random-poisson" -body {
1008    set rnumbers [::math::statistics::random-poisson 2.5 100]
1009    set result [llength $rnumbers]
1010} -result 100
1011
1012test "random-numbers-1.5" "Test random-chisquare" -body {
1013    set rnumbers [::math::statistics::random-chisquare 3 100]
1014    set result [llength $rnumbers]
1015} -result 100
1016
1017test "random-numbers-1.6" "Test random-students-t" -body {
1018    set rnumbers [::math::statistics::random-students-t 3 100]
1019    set result [llength $rnumbers]
1020} -result 100
1021
1022test "random-numbers-1.7" "Test random-beta" -body {
1023    set rnumbers [::math::statistics::random-beta 1.3 2.4 100]
1024    set result 1
1025    foreach r $rnumbers {
1026        if { $r < 0.0 || $r > 1.0 } {
1027            result 0
1028            break
1029        }
1030    }
1031    lappend result [llength $rnumbers]
1032} -result {1 100}
1033
1034test "random-numbers-1.8" "Test random-gumbel" -body {
1035    set rnumbers [::math::statistics::random-gumbel 1.0 3.0 100]
1036    set result [llength $rnumbers]
1037} -result 100
1038
1039test "random-numbers-1.9" "Test random-weibull" -body {
1040    set rnumbers [::math::statistics::random-weibull 1.0 3.0 100]
1041    set result [llength $rnumbers]
1042} -result 100
1043
1044test "random-numbers-1.10" "Test random-pareto" -body {
1045    set rnumbers [::math::statistics::random-pareto 1.0 3.0 100]
1046    set result 1
1047    foreach r $rnumbers {
1048        if { $r < 1.0 } {
1049            result 0
1050            break
1051        }
1052    }
1053    lappend result [llength $rnumbers]
1054} -result {1 100}
1055
1056test "random-numbers-1.11" "Test random-lognormal" -body {
1057    set rnumbers [::math::statistics::random-lognormal 1 1 100]
1058    set result [llength $rnumbers]
1059} -result 100
1060
1061test "random-numbers-1.11" "Test random-cauchy" -body {
1062    set rnumbers [::math::statistics::random-cauchy 0 1 100]
1063    set result [llength $rnumbers]
1064} -result 100
1065
1066test "random-numbers-1.12" "Test random-triangular" -body {
1067    set result 1
1068    set rnumbers [::math::statistics::random-triangular -10 10 100]
1069    # Check the scaling
1070    foreach r $rnumbers {
1071        if { $r < -10.0 || $r > 10.0 } {
1072            set result 0
1073            break
1074        }
1075    }
1076
1077    # Also the alternative triangle
1078    set rnumbers [::math::statistics::random-triangular 10 -10 100]
1079    # Check the scaling
1080    foreach r $rnumbers {
1081        if { $r < -10.0 || $r > 10.0 } {
1082            set result 0
1083            break
1084        }
1085    }
1086
1087    # The symmetric triangle
1088    set rnumbers [::math::statistics::random-symmetric-triangular 10 -10 100]
1089    # Check the scaling
1090    foreach r $rnumbers {
1091        if { $r < -10.0 || $r > 10.0 } {
1092            set result 0
1093            break
1094        }
1095    }
1096
1097    set result
1098} -result 1
1099
1100test "random-numbers-2.1" "Test random/estimate-pareto" -match tolerant -body {
1101    expr {srand($::rseed)}
1102    set rnumbers [::math::statistics::random-pareto 1.0 3.0 100]
1103    set result   [::math::statistics::estimate-pareto $rnumbers]
1104} -result {1.000519 3.668162 0.3668162}
1105
1106test "random-numbers-2.2" "Test random/estimate-exponential" -match tolerant -body {
1107    expr {srand($::rseed)}
1108    set rnumbers [::math::statistics::random-exponential 2.0 1000]
1109    set result   [::math::statistics::estimate-exponential $rnumbers]
1110} -result {1.9976327295438587 0.06317069353857727}
1111
1112test "random-numbers-2.3" "Test random/estimate-laplace" -match tolerant -body {
1113    expr {srand($::rseed)}
1114    set rnumbers [::math::statistics::random-laplace 1.0 1.0 1000]
1115    set result   [::math::statistics::estimate-laplace $rnumbers]
1116} -result {1.0106101707362272 0.9319576942526239}
1117
1118# Very simple test - just check that the numbers lie between 0 and 1
1119test "random-numbers-2.4" "Test random-kumaraswamy" -body {
1120    expr {srand($::rseed)}
1121    set result   1
1122
1123    foreach {a b} {0.5 0.5  1.0 1.0  0.5 1.0  1.0 0.5  2.0 0.1  0.1 2.0} {
1124        set rnumbers [::math::statistics::random-kumaraswamy $a $b 1000]
1125        foreach r $rnumbers {
1126            if { $r < 0.0 || $r > 1.0 } {
1127                set result 0
1128                break
1129            }
1130        }
1131    }
1132    set result
1133} -result 1
1134
1135# Simple test, exact matching
1136test "random-numbers-2.5" "Test random-negative-binomial" -body {
1137    expr {srand($::rseed)}
1138    set rnumbers [::math::statistics::random-negative-binomial 1 0.3 10]
1139} -result {0 0 3 0 0 0 1 0 1 1}
1140
1141test "random-numbers-2.6" "Test random/estimate-negative-binomial" -match tolerant -body {
1142    expr {srand($::rseed)}
1143    set r        3
1144    set rnumbers [::math::statistics::random-negative-binomial $r 0.5 1000]
1145    set pvalue   [::math::statistics::estimate-negative-binomial $r $rnumbers]
1146} -result 0.50190935
1147
1148test "kruskal-wallis-1.0" "Test analysis Kruskal-Wallis" -match tolerant -body {
1149    ::math::statistics::analyse-Kruskal-Wallis {6.4 6.8 7.2 8.3 8.4 9.1 9.4 9.7} {2.5 3.7 4.9 5.4 5.9 8.1 8.2} {1.3 4.1 4.9 5.2 5.5 8.2}
1150} -result {9.83627087199 0.00731275323967}
1151test "kruskal-wallis-1.1" "Test test Kruskal-Wallis" -match tolerant -body {
1152    ::math::statistics::test-Kruskal-Wallis 0.95 {6.4 6.8 7.2 8.3 8.4 9.1 9.4 9.7} {2.5 3.7 4.9 5.4 5.9 8.1 8.2} {1.3 4.1 4.9 5.2 5.5 8.2}
1153} -result 1
1154
1155# Data from Statistical methods in Engineering and Quality Assurance by Peter W.M. John
1156test "wilcoxon-1.0" "Test test Wilcoxon" -match tolerant -body {
1157    ::math::statistics::test-Wilcoxon {71.1 68.3 74.8 72.1 71.2 70.4 73.6 66.3 72.7 74.1 70.1 68.5} \
1158                                      {73.3 70.9 74.6 72.1 72.8 74.2 74.7 69.2 75.5 75.8 70.0 72.1}
1159} -result -1.67431578065
1160
1161# Very simple tests, merely to show that the procedure "works"
1162# (No numerical example available)
1163test "levene-brown-forsythe-1.0" "Test test Levene/Brown-Forsythe" -match tolerant -body {
1164    set values {{1 2 3} {2 3 4} {5 6 7}}
1165    ::math::statistics::test-Levene $values
1166} -result 0.0
1167
1168test "levene-brown-forsythe-1.1" "Test test Levene/Brown-Forsythe" -match tolerant -body {
1169    set values {{1 2 3} {2 3 4} {5 6 7}}
1170    ::math::statistics::test-Brown-Forsythe $values
1171} -result 0.0
1172
1173test "levene-brown-forsythe-1.2" "Test test Levene/Brown-Forsythe" -match tolerant -body {
1174    set values {{1 2 2} {2 3 4} {5 6 7.4}}
1175    ::math::statistics::test-Levene $values
1176} -result 0.47937131630648294
1177
1178test "levene-brown-forsythe-1.3" "Test test Levene/Brown-Forsythe" -match tolerant -body {
1179    set values {{1 2 2} {2 3 4} {5 6 7.4}}
1180    ::math::statistics::test-Brown-Forsythe $values
1181} -result 0.4382022471910113
1182
1183# Data taken from https://www.itl.nist.gov/div898/handbook/eda/section3/eda35a.htm
1184# Note: the webpage talks of the Levene test, but is actually the Brown-Forsythe
1185test "levene-brown-forsythe-1.4" "Test test Levene/Brown-Forsythe" -match tolerant -body {
1186    set values {{ 1.006 0.996 0.998 1.000 0.992 0.993 1.002 0.999 0.994 1.000 }
1187                { 0.998 1.006 1.000 1.002 0.997 0.998 0.996 1.000 1.006 0.988 }
1188                { 0.991 0.987 0.997 0.999 0.995 0.994 1.000 0.999 0.996 0.996 }
1189                { 1.005 1.002 0.994 1.000 0.995 0.994 0.998 0.996 1.002 0.996 }
1190                { 0.998 0.998 0.982 0.990 1.002 0.984 0.996 0.993 0.980 0.996 }
1191                { 1.009 1.013 1.009 0.997 0.988 1.002 0.995 0.998 0.981 0.996 }
1192                { 0.990 1.004 0.996 1.001 0.998 1.000 1.018 1.010 0.996 1.002 }
1193                { 0.998 1.000 1.006 1.000 1.002 0.996 0.998 0.996 1.002 1.006 }
1194                { 1.002 0.998 0.996 0.995 0.996 1.004 1.004 0.998 0.999 0.991 }
1195               { 0.991 0.995 0.984 0.994 0.997 0.997 0.991 0.998 1.004 0.997 } }
1196    ::math::statistics::test-Brown-Forsythe $values
1197} -result 1.705910
1198
1199# Data from the Wikipedia page on Spearman's rank correlation coefficient
1200test "spearman-rank-1.0" "Test Spearman rank correlation" -match tolerant -body {
1201    ::math::statistics::spearman-rank {106  86 100 101  99 103  97 113 112 110} \
1202                                      {  7   0  27  50  28  29  20  12   6  17}
1203} -result -0.175757575758
1204
1205test "spearman-rank-extended-1.0" "Test extended Spearman rank correlation procedure" -match tolerant -body {
1206    ::math::statistics::spearman-rank-extended {106  86 100 101  99 103  97 113 112 110} \
1207                                               {  7   0  27  50  28  29  20  12   6  17}
1208} -result {-0.175757575758 10 -0.456397284}
1209
1210#
1211# Note: for the uniform and the logistic kernel the sum deviates more from 1 than for the others.
1212# For the logistic kernel this is because the density function is very widespread. For the
1213# uniform kernel the reason is not quite clear. Hence the margin per kernel.
1214#
1215test "kernel-density-1.0" "Test various kernel functions" -body {
1216    set data {1 2 3 4 5 6 7 8 9 10}
1217
1218    set roughlyOne {}
1219
1220    foreach kernel {gaussian uniform triangular epanechnikov biweight cosine logistic} \
1221            margin {0.01     0.02    0.01       0.01         0.01     0.01   0.05    } {
1222        set result [::math::statistics::kernel-density $data -kernel $kernel]
1223
1224        set sum 0.0
1225        set xbegin [lindex $result 2 0]
1226        set xend   [lindex $result 2 1]
1227        set number [llength [lindex $result 0]]
1228        set dx     [expr {($xend-$xbegin) / $number}]
1229
1230        #
1231        # Integral should be roughly one
1232        #
1233        set sum 0.0
1234        foreach v [lindex $result 1] {
1235            set sum [expr {$sum + $dx * $v}]
1236        }
1237
1238        lappend roughlyOne [expr {abs($sum-1.0) < $margin}]
1239    }
1240
1241    return $roughlyOne
1242} -result {1 1 1 1 1 1 1}
1243
1244test "kernel-density-1.1" "Test various options - just that they have effect" -body {
1245    set subResults {}
1246
1247    set data {1 2 3 4 5 6 7 8 9 10}
1248
1249    set result [::math::statistics::kernel-density $data -number 20]
1250    lappend subResults [llength [lindex $result 0]]  ;# Number of bins
1251    lappend subResults [llength [lindex $result 1]]  ;# Number of density values
1252
1253    set result [::math::statistics::kernel-density $data -interval {0 20}]
1254    lappend subResults [lindex $result 2 0]          ;# Beginning of interval
1255    lappend subResults [lindex $result 2 1]          ;# End of interval
1256    lappend subResults [expr {[lindex $result 0 0]   > [lindex $result 2 0]}] ;# First bin -- beginning of interval
1257    lappend subResults [expr {[lindex $result 0 0]   < [lindex $result 2 1]}] ;# First bin -- end of interval
1258    lappend subResults [expr {[lindex $result 0 end] > [lindex $result 2 0]}] ;# Last bin -- beginning of interval
1259    lappend subResults [expr {[lindex $result 0 end] < [lindex $result 2 1]}] ;# Last bin -- end of interval
1260
1261    set result [::math::statistics::kernel-density $data -bandwidth 2]
1262    lappend subResults [lindex $result 2 end]        ;# Bandwidth
1263
1264    return $subResults
1265} -result {20 20 0 20 1 1 1 1 2}
1266
1267test "kernel-density-1.2" "Dealing with missing values" -body {
1268    set subResults {}
1269
1270    set data {1 2 3 4 {} 6 7 8 9 10}
1271
1272    set result [::math::statistics::kernel-density $data]
1273
1274    set sum 0.0
1275    set xbegin [lindex $result 2 0]
1276    set xend   [lindex $result 2 1]
1277    set number [llength [lindex $result 0]]
1278    set dx     [expr {($xend-$xbegin) / $number}]
1279
1280    #
1281    # Integral should be roughly one
1282    #
1283    set sum 0.0
1284    foreach v [lindex $result 1] {
1285        set sum [expr {$sum + $dx * $v}]
1286    }
1287
1288    return [expr {abs($sum-1.0) < 0.01}]
1289} -result 1
1290
1291test "anova-one-way-1.1" "ANOVA test from Wikipedia" -body {
1292    ::math::statistics::test-anova-F 0.05 {6 8 4 5 3 4} {8 12 9 11 6 8} {13 9 11 8 7 12}
1293} -result 0
1294
1295test "anova-one-way-1.2" "ANOVA test from Wikipedia - using nested list" -body {
1296    ::math::statistics::test-anova-F 0.05 {{6 8 4 5 3 4} {8 12 9 11 6 8} {13 9 11 8 7 12}}
1297} -result 0
1298
1299#
1300# Data from http://www.itl.nist.gov/div898/handbook/prc/section4/prc436.htm#example1
1301# See also http://www.itl.nist.gov/div898/handbook/prc/section4/prc471.htm
1302# Caveat: the calculation produces slightly different confidence intervals. I checked whether
1303# I got the calculation of the pooled variance right against the example appearing on Wikipedia
1304# (https://en.wikipedia.org/wiki/Pooled_variance) and that seems okay.
1305# No idea where the numerical difference is coming from.
1306#
1307test "Tukey-range-test-1.1" "Tukey range test" -body {
1308    set data {
1309        Group 1        {6.9     5.4     5.8     4.6     4.0}
1310        Group 2        {8.3     6.8     7.8     9.2     6.5}
1311        Group 3        {8.0     10.5    8.1     6.9     9.3}
1312        Group 4        {5.8     3.8     6.1     5.6     6.2}
1313    }
1314
1315    set groupData {}
1316    foreach {dummy label d} $data {
1317        lappend groupData $d
1318    }
1319
1320    set tukeyRange [::math::statistics::test-Tukey-range 0.05 $groupData]
1321
1322    set indications {}
1323    foreach t $tukeyRange {
1324        lappend indications [lindex $t 2]
1325    }
1326    set indications
1327} -result {1 1 0 0 0 1}
1328
1329#
1330# Data from https://en.wikipedia.org/wiki/Dunnett's_test
1331# Note that the Wikipedia uses t = 2.94, whereas it should have been 2.88
1332#
1333test "Dunnet-test-1.1" "Dunnett test" -body {
1334    set control {55 47 48}
1335    set data    {{55 64 64} {55 49 52} {50 44 41}}
1336
1337    set dunnett [::math::statistics::test-Dunnett 0.05 $control $data]
1338
1339    set indications {}
1340    foreach t $dunnett {
1341        lappend indications [lindex $t 0]
1342    }
1343    set indications
1344} -result {1 0 0}
1345
1346#
1347# Bootstrap method to create new samples
1348#
1349test "Bootstrap-1.1" "Bootstrap - construct a single sample" -body {
1350    set data {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
1351
1352    set sample [::math::statistics::bootstrap $data 10]
1353    #
1354    # Check the sample
1355    #
1356    set numberOkay 0
1357    foreach value $sample {
1358        incr numberOkay [expr {$value in $data}]
1359    }
1360
1361    set result [list [llength $sample] $numberOkay]
1362} -result {10 10}
1363
1364test "Bootstrap-1.2" "Bootstrap - variation in a single sample" -body {
1365    set data 0
1366    for {set i 0} {$i < 100} {incr i} {
1367        lappend data $i
1368    }
1369
1370    set sample [::math::statistics::bootstrap $data 10]
1371    #
1372    # It is improbable that all values are the same
1373    #
1374    set numberInHalf1 0
1375    set numberInHalf2 0
1376    set halfData1 [lrange $data  0 49]
1377    set halfData2 [lrange $data 50 99]
1378    foreach value $sample {
1379        incr numberInHalf1 [expr {$value in $halfData1}]
1380        incr numberInHalf2 [expr {$value in $halfData2}]
1381    }
1382
1383    set result [list [expr {$numberInHalf1 > 0}] [expr {$numberInHalf2 > 0}]]
1384} -result {1 1}
1385
1386test "Bootstrap-1.3" "Bootstrap - list of samples" -body {
1387    set data 0
1388    for {set i 0} {$i < 100} {incr i} {
1389        lappend data $i
1390    }
1391
1392    set sampleSize    10
1393    set numberSamples 20
1394    set samples [::math::statistics::bootstrap $data $sampleSize $numberSamples]
1395
1396    set result 1
1397
1398    if { [llength $samples] != $numberSamples } {
1399        set result 0
1400    }
1401
1402    foreach sample $samples {
1403        if { [llength $sample] != $sampleSize } {
1404            set result 0
1405        }
1406    }
1407
1408    set result
1409} -result 1
1410
1411
1412#
1413# Tests for Wasserstein distance and KL divergence
1414#
1415# Tests for error conditions
1416#
1417test "Wasserstein-1.1" "Error: lengths differ" -body {
1418    set distance [::math::statistics::wasserstein-distance {0 1} {1 0 0 0}]
1419} -returnCodes 1 -result {Lengths of the probability histograms must be the same}
1420
1421#
1422# Check the symmetry for arbitrary histograms
1423#
1424test "Wasserstein-1.2" "Test symmetry" -body {
1425    set okay 1
1426    for {set i 0} {$i < 10} {incr i} {
1427        set histogram1 {}
1428        set histogram2 {}
1429        for {set j 0} {$j < 3*($i+1)} {incr j} {
1430            lappend histogram1 [expr {rand()}]
1431            lappend histogram2 [expr {rand()}]
1432        }
1433
1434        set distance1 [::math::statistics::wasserstein-distance $histogram1 $histogram2]
1435        set distance2 [::math::statistics::wasserstein-distance $histogram2 $histogram1]
1436
1437        if { abs($distance1-$distance2) > 1.0e-6 } {
1438            set okay 0
1439        }
1440    }
1441
1442    return $okay
1443
1444} -result 1
1445
1446#
1447# Check the non-negativity for arbitrary histograms
1448#
1449test "Wasserstein-1.3" "Test non-negativity" -body {
1450    set okay 1
1451    for {set i 0} {$i < 10} {incr i} {
1452        set histogram1 {}
1453        set histogram2 {}
1454        for {set j 0} {$j < 3*($i+1)} {incr j} {
1455            lappend histogram1 [expr {rand()}]
1456            lappend histogram2 [expr {rand()}]
1457        }
1458
1459        set distance1 [::math::statistics::wasserstein-distance $histogram1 $histogram2]
1460
1461        if { $distance1 < 0.0 } {
1462            set okay 0
1463        }
1464    }
1465
1466    return $okay
1467
1468} -result 1
1469
1470#
1471# Check the non-normalised histograms
1472#
1473test "Wasserstein-1.4" "Test non-normalised histograms" -match tolerant -body {
1474    return [list [::math::statistics::wasserstein-distance {2 0 0} {0 0 0.5}]  \
1475                 [::math::statistics::wasserstein-distance {1 0 0} {0 0 0.25}] ]
1476} -result {2.0 2.0}
1477
1478#
1479# Check arbitrarily extended histograms
1480#
1481test "Wasserstein-1.5" "Test extended histograms" -match tolerant -body {
1482    return [list [::math::statistics::wasserstein-distance {1 0 0}     {0 0 1}]     \
1483                 [::math::statistics::wasserstein-distance {0 0 1 0 0} {0 0 0 0 1}] \
1484                 [::math::statistics::wasserstein-distance {1 0 0 0 0} {0 0 1 0 0}] ]
1485} -result {2.0 2.0 2.0}
1486
1487#
1488# Check numerical results
1489#
1490test "Wasserstein-1.6" "Test numerical results" -match tolerant -body {
1491    return [list [::math::statistics::wasserstein-distance {1 0 0}     {0 0.5 0.5}]             \
1492                 [::math::statistics::wasserstein-distance {1 0 0 0 0} {0 0 0 0.5 0.5}]         \
1493                 [::math::statistics::wasserstein-distance {1 0 0 0 0} {0 0.25 0.25 0.25 0.25}] ]
1494} -result {1.5 3.5 2.5}
1495
1496#
1497# Tests for error conditions
1498#
1499test "KL-divergence-1.1" "Error: lengths differ" -body {
1500    set distance [::math::statistics::kl-divergence {0 1} {1 0 0 0}]
1501} -returnCodes 1 -result {Lengths of the two probability histograms must be the same}
1502
1503test "KL-divergence-1.2" "Error: unmatched zeroes" -body {
1504    set distance [::math::statistics::kl-divergence {0.3 0.3 0.4} {1 0 0}]
1505} -returnCodes 1 -result {Second probability histogram contains unmatched zeroes}
1506
1507
1508test "KL-divergence-1.3" "Matched zeroes should be accepted" -match tolerant -body {
1509    set distance [::math::statistics::kl-divergence {0.3 0.3 0.4 0} {0.7 0.2 0.1 0}]
1510} -returnCodes 0 -result 0.42196792
1511
1512#
1513# Tests for equal histograms (not all normalised)
1514#
1515test "KL-divergence-1.4" "Equal histograms give zero divergence" -body {
1516    set distance [::math::statistics::kl-divergence {0.3 0.3 0.4} {0.3 0.3 0.4}]
1517} -result 0.0
1518
1519test "KL-divergence-1.5" "Non-normalised but equal histograms give zero divergence" -body {
1520    set distance [::math::statistics::kl-divergence {0.3 0.3 0.4} {0.6 0.6 0.8}]
1521} -result 0.0
1522
1523#
1524# Numerical tests - note: the expected values were taken from the implementation
1525#                         No independent source found
1526#
1527test "KL-divergence-1.6" "Shifted histograms" -match tolerant -body {
1528    set distance [::math::statistics::kl-divergence {1.0e-8 0.3 0.3 0.3 0.1} {0.3 0.3 0.3 0.1 1.0e-8}]
1529} -result 1.9413931
1530
1531test "KL-divergence-1.7" "Arbitrary histograms" -match tolerant -body {
1532    set distance [::math::statistics::kl-divergence {0.4 0.2 0.3 0.1} {0.1 0.1 0.7 0.1}]
1533} -result 0.4389578
1534
1535
1536#
1537# Tests for logistic regression
1538#
1539set xdata {0.50 0.75 1.00 1.25 1.50 1.75 1.75 2.00 2.25 2.50 2.75 3.00 3.25 3.50 4.00 4.25 4.50 4.75 5.00 5.50}
1540set ydata {0    0    0    0    0    0    1    0    1    0    1    0    1    0    1    1    1    1    1    1   }
1541
1542test "Logit-regression-1.0" "Logistic regression - coefficients" -match tolerant -body {
1543    set coeffs [::math::statistics::logistic-model $xdata $ydata]
1544} -result {-4.07679035286303 1.5045982920571572}
1545
1546test "Logit-regression-1.1" "Logistic regression - probabilities" -match tolerant -body {
1547    set coeffs [::math::statistics::logistic-model $xdata $ydata]
1548
1549    set probabilities {}
1550    foreach x {1 2 3 4 5} {
1551        lappend probabilities [::math::statistics::logistic-probability $coeffs $x]
1552    }
1553
1554    return $probabilities
1555
1556} -result {0.07094967663389527 0.2558609520815849 0.6075450376084848 0.8745281239093334 0.9691176473773739}
1557
1558
1559# End of test cases
1560testsuiteCleanup
1561