File Coverage

blib/lib/Statistics/Distributions.pm
Criterion Covered Total %
statement 131 225 58.2
branch 43 98 43.8
condition 17 51 33.3
subroutine 24 26 92.3
pod 0 13 0.0
total 215 413 52.0


line stmt bran cond sub pod time code
1             package Statistics::Distributions;
2              
3 1     1   614 use strict;
  1         1  
  1         32  
4 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         2  
  1         78  
5 1     1   7 use constant PI => 3.1415926536;
  1         5  
  1         76  
6 1     1   4 use constant SIGNIFICANT => 5; # number of significant digits to be returned
  1         2  
  1         2859  
7              
8             require Exporter;
9              
10             @ISA = qw(Exporter AutoLoader);
11             # Items to export into callers namespace by default. Note: do not export
12             # names by default without a very good reason. Use EXPORT_OK instead.
13             # Do not simply export all your public functions/methods/constants.
14             @EXPORT_OK = qw(chisqrdistr tdistr fdistr udistr uprob chisqrprob tprob fprob);
15             $VERSION = '1.02';
16              
17             # Preloaded methods go here.
18            
19             sub chisqrdistr { # Percentage points X^2(x^2,n)
20 1     1 0 46 my ($n, $p) = @_;
21 1 50 33     11 if ($n <= 0 || abs($n) - abs(int($n)) != 0) {
22 0         0 die "Invalid n: $n\n"; # degree of freedom
23             }
24 1 50 33     10 if ($p <= 0 || $p > 1) {
25 0         0 die "Invalid p: $p\n";
26             }
27 1         7 return precision_string(_subchisqr($n, $p));
28             }
29              
30             sub udistr { # Percentage points N(0,1^2)
31 1     1 0 32 my ($p) = (@_);
32 1 50 33     9 if ($p > 1 || $p <= 0) {
33 0         0 die "Invalid p: $p\n";
34             }
35 1         5 return precision_string(_subu($p));
36             }
37              
38             sub tdistr { # Percentage points t(x,n)
39 1     1 0 28 my ($n, $p) = @_;
40 1 50 33     10 if ($n <= 0 || abs($n) - abs(int($n)) != 0) {
41 0         0 die "Invalid n: $n\n";
42             }
43 1 50 33     8 if ($p <= 0 || $p >= 1) {
44 0         0 die "Invalid p: $p\n";
45             }
46 1         5 return precision_string(_subt($n, $p));
47             }
48              
49             sub fdistr { # Percentage points F(x,n1,n2)
50 1     1 0 31 my ($n, $m, $p) = @_;
51 1 50 33     10 if (($n<=0) || ((abs($n)-(abs(int($n))))!=0)) {
52 0         0 die "Invalid n: $n\n"; # first degree of freedom
53             }
54 1 50 33     11 if (($m<=0) || ((abs($m)-(abs(int($m))))!=0)) {
55 0         0 die "Invalid m: $m\n"; # second degree of freedom
56             }
57 1 50 33     10 if (($p<=0) || ($p>1)) {
58 0         0 die "Invalid p: $p\n";
59             }
60 1         5 return precision_string(_subf($n, $m, $p));
61             }
62              
63             sub uprob { # Upper probability N(0,1^2)
64 1     1 0 31 my ($x) = @_;
65 1         6 return precision_string(_subuprob($x));
66             }
67              
68             sub chisqrprob { # Upper probability X^2(x^2,n)
69 1     1 0 31 my ($n,$x) = @_;
70 1 50 33     9 if (($n <= 0) || ((abs($n) - (abs(int($n)))) != 0)) {
71 0         0 die "Invalid n: $n\n"; # degree of freedom
72             }
73 1         4 return precision_string(_subchisqrprob($n, $x));
74             }
75              
76             sub tprob { # Upper probability t(x,n)
77 1     1 0 30 my ($n, $x) = @_;
78 1 50 33     9 if (($n <= 0) || ((abs($n) - abs(int($n))) !=0)) {
79 0         0 die "Invalid n: $n\n"; # degree of freedom
80             }
81 1         3 return precision_string(_subtprob($n, $x));
82             }
83              
84             sub fprob { # Upper probability F(x,n1,n2)
85 1     1 0 31 my ($n, $m, $x) = @_;
86 1 50 33     12 if (($n<=0) || ((abs($n)-(abs(int($n))))!=0)) {
87 0         0 die "Invalid n: $n\n"; # first degree of freedom
88             }
89 1 50 33     18 if (($m<=0) || ((abs($m)-(abs(int($m))))!=0)) {
90 0         0 die "Invalid m: $m\n"; # second degree of freedom
91             }
92 1         4 return precision_string(_subfprob($n, $m, $x));
93             }
94              
95              
96             sub _subfprob {
97 1     1   2 my ($n, $m, $x) = @_;
98 1         1 my $p;
99              
100 1 50       7 if ($x<=0) {
    50          
    0          
101 0         0 $p=1;
102             } elsif ($m % 2 == 0) {
103 1         3 my $z = $m / ($m + $n * $x);
104 1         3 my $a = 1;
105 1         10 for (my $i = $m - 2; $i >= 2; $i -= 2) {
106 2         13 $a = 1 + ($n + $i - 2) / $i * $z * $a;
107             }
108 1         10 $p = 1 - ((1 - $z) ** ($n / 2) * $a);
109             } elsif ($n % 2 == 0) {
110 0         0 my $z = $n * $x / ($m + $n * $x);
111 0         0 my $a = 1;
112 0         0 for (my $i = $n - 2; $i >= 2; $i -= 2) {
113 0         0 $a = 1 + ($m + $i - 2) / $i * $z * $a;
114             }
115 0         0 $p = (1 - $z) ** ($m / 2) * $a;
116             } else {
117 0         0 my $y = atan2(sqrt($n * $x / $m), 1);
118 0         0 my $z = sin($y) ** 2;
119 0 0       0 my $a = ($n == 1) ? 0 : 1;
120 0         0 for (my $i = $n - 2; $i >= 3; $i -= 2) {
121 0         0 $a = 1 + ($m + $i - 2) / $i * $z * $a;
122             }
123 0         0 my $b = PI;
124 0         0 for (my $i = 2; $i <= $m - 1; $i += 2) {
125 0         0 $b *= ($i - 1) / $i;
126             }
127 0         0 my $p1 = 2 / $b * sin($y) * cos($y) ** $m * $a;
128              
129 0         0 $z = cos($y) ** 2;
130 0 0       0 $a = ($m == 1) ? 0 : 1;
131 0         0 for (my $i = $m-2; $i >= 3; $i -= 2) {
132 0         0 $a = 1 + ($i - 1) / $i * $z * $a;
133             }
134 0         0 $p = max(0, $p1 + 1 - 2 * $y / PI
135             - 2 / PI * sin($y) * cos($y) * $a);
136             }
137 1         3 return $p;
138             }
139              
140              
141             sub _subchisqrprob {
142 1     1   2 my ($n,$x) = @_;
143 1         2 my $p;
144              
145 1 50       10 if ($x <= 0) {
    50          
    50          
146 0         0 $p = 1;
147             } elsif ($n > 100) {
148 0         0 $p = _subuprob((($x / $n) ** (1/3)
149             - (1 - 2/9/$n)) / sqrt(2/9/$n));
150             } elsif ($x > 400) {
151 0         0 $p = 0;
152             } else {
153 1         2 my ($a, $i, $i1);
154 1 50       3 if (($n % 2) != 0) {
155 1         3 $p = 2 * _subuprob(sqrt($x));
156 1         4 $a = sqrt(2/PI) * exp(-$x/2) / sqrt($x);
157 1         11 $i1 = 1;
158             } else {
159 0         0 $p = $a = exp(-$x/2);
160 0         0 $i1 = 2;
161             }
162              
163 1         5 for ($i = $i1; $i <= ($n-2); $i += 2) {
164 1         2 $a *= $x / $i;
165 1         4 $p += $a;
166             }
167             }
168 1         3 return $p;
169             }
170              
171             sub _subu {
172 3     3   5 my ($p) = @_;
173 3         9 my $y = -log(4 * $p * (1 - $p));
174 3         15 my $x = sqrt(
175             $y * (1.570796288
176             + $y * (.03706987906
177             + $y * (-.8364353589E-3
178             + $y *(-.2250947176E-3
179             + $y * (.6841218299E-5
180             + $y * (0.5824238515E-5
181             + $y * (-.104527497E-5
182             + $y * (.8360937017E-7
183             + $y * (-.3231081277E-8
184             + $y * (.3657763036E-10
185             + $y *.6936233982E-12)))))))))));
186 3 100       10 $x = -$x if ($p>.5);
187 3         8 return $x;
188             }
189              
190             sub _subuprob {
191 2     2   3 my ($x) = @_;
192 2         3 my $p = 0; # if ($absx > 100)
193 2         3 my $absx = abs($x);
194              
195 2 100       10 if ($absx < 1.9) {
    50          
196 1         17 $p = (1 +
197             $absx * (.049867347
198             + $absx * (.0211410061
199             + $absx * (.0032776263
200             + $absx * (.0000380036
201             + $absx * (.0000488906
202             + $absx * .000005383)))))) ** -16/2;
203             } elsif ($absx <= 100) {
204 1         4 for (my $i = 18; $i >= 1; $i--) {
205 18         37 $p = $i / ($absx + $p);
206             }
207 1         4 $p = exp(-.5 * $absx * $absx)
208             / sqrt(2 * PI) / ($absx + $p);
209             }
210              
211 2 100       9 $p = 1 - $p if ($x<0);
212 2         6 return $p;
213             }
214              
215            
216             sub _subt {
217 4     4   6 my ($n, $p) = @_;
218              
219 4 50 33     23 if ($p >= 1 || $p <= 0) {
220 0         0 die "Invalid p: $p\n";
221             }
222              
223 4 50       17 if ($p == 0.5) {
    100          
224 0         0 return 0;
225             } elsif ($p < 0.5) {
226 2         20 return - _subt($n, 1 - $p);
227             }
228              
229 2         4 my $u = _subu($p);
230 2         5 my $u2 = $u ** 2;
231              
232 2         5 my $a = ($u2 + 1) / 4;
233 2         5 my $b = ((5 * $u2 + 16) * $u2 + 3) / 96;
234 2         5 my $c = (((3 * $u2 + 19) * $u2 + 17) * $u2 - 15) / 384;
235 2         5 my $d = ((((79 * $u2 + 776) * $u2 + 1482) * $u2 - 1920) * $u2 - 945)
236             / 92160;
237 2         16 my $e = (((((27 * $u2 + 339) * $u2 + 930) * $u2 - 1782) * $u2 - 765) * $u2
238             + 17955) / 368640;
239              
240 2         7 my $x = $u * (1 + ($a + ($b + ($c + ($d + $e / $n) / $n) / $n) / $n) / $n);
241              
242 2 50       5 if ($n <= log10($p) ** 2 + 3) {
243 2         3 my $round;
244 2   66     3 do {
245 7         14 my $p1 = _subtprob($n, $x);
246 7         12 my $n1 = $n + 1;
247 7         50 my $delta = ($p1 - $p)
248             / exp(($n1 * log($n1 / ($n + $x * $x))
249             + log($n/$n1/2/PI) - 1
250             + (1/$n1 - 1/$n) / 6) / 2);
251 7         9 $x += $delta;
252 7         14 $round = sprintf("%.".abs(int(log10(abs $x)-4))."f",$delta);
253             } while (($x) && ($round != 0));
254             }
255 2         17 return $x;
256             }
257              
258             sub _subtprob {
259 8     8   11 my ($n, $x) = @_;
260              
261 8         15 my ($a,$b);
262 8         44 my $w = atan2($x / sqrt($n), 1);
263 8         36 my $z = cos($w) ** 2;
264 8         12 my $y = 1;
265              
266 8         23 for (my $i = $n-2; $i >= 2; $i -= 2) {
267 0         0 $y = 1 + ($i-1) / $i * $z * $y;
268             }
269              
270 8 50       18 if ($n % 2 == 0) {
271 0         0 $a = sin($w)/2;
272 0         0 $b = .5;
273             } else {
274 8 100       19 $a = ($n == 1) ? 0 : sin($w)*cos($w)/PI;
275 8         11 $b= .5 + $w/PI;
276             }
277 8         51 return max(0, 1 - $b - $a * $y);
278             }
279              
280             sub _subf {
281 1     1   2 my ($n, $m, $p) = @_;
282 1         2 my $x;
283              
284 1 50 33     9 if ($p >= 1 || $p <= 0) {
285 0         0 die "Invalid p: $p\n";
286             }
287              
288 1 50       43 if ($p == 1) {
    50          
    50          
    0          
    0          
289 0         0 $x = 0;
290             } elsif ($m == 1) {
291 0         0 $x = 1 / (_subt($n, 0.5 - $p / 2) ** 2);
292             } elsif ($n == 1) {
293 1         11 $x = _subt($m, $p/2) ** 2;
294             } elsif ($m == 2) {
295 0         0 my $u = _subchisqr($m, 1 - $p);
296 0         0 my $a = $m - 2;
297 0         0 $x = 1 / ($u / $m * (1 +
298             (($u - $a) / 2 +
299             (((4 * $u - 11 * $a) * $u + $a * (7 * $m - 10)) / 24 +
300             (((2 * $u - 10 * $a) * $u + $a * (17 * $m - 26)) * $u
301             - $a * $a * (9 * $m - 6)
302             )/48/$n
303             )/$n
304             )/$n));
305             } elsif ($n > $m) {
306 0         0 $x = 1 / _subf2($m, $n, 1 - $p)
307             } else {
308 0         0 $x = _subf2($n, $m, $p)
309             }
310 1         6 return $x;
311             }
312              
313             sub _subf2 {
314 0     0   0 my ($n, $m, $p) = @_;
315 0         0 my $u = _subchisqr($n, $p);
316 0         0 my $n2 = $n - 2;
317 0         0 my $x = $u / $n *
318             (1 +
319             (($u - $n2) / 2 +
320             (((4 * $u - 11 * $n2) * $u + $n2 * (7 * $n - 10)) / 24 +
321             (((2 * $u - 10 * $n2) * $u + $n2 * (17 * $n - 26)) * $u
322             - $n2 * $n2 * (9 * $n - 6)) / 48 / $m) / $m) / $m);
323 0         0 my $delta;
324 0         0 do {
325 0         0 my $z = exp(
326             (($n+$m) * log(($n+$m) / ($n * $x + $m))
327             + ($n - 2) * log($x)
328             + log($n * $m / ($n+$m))
329             - log(4 * PI)
330             - (1/$n + 1/$m - 1/($n+$m))/6
331             )/2);
332 0         0 $delta = (_subfprob($n, $m, $x) - $p) / $z;
333 0         0 $x += $delta;
334             } while (abs($delta)>3e-4);
335 0         0 return $x;
336             }
337              
338             sub _subchisqr {
339 1     1   2 my ($n, $p) = @_;
340 1         3 my $x;
341              
342 1 50 33     18 if (($p > 1) || ($p <= 0)) {
    50          
    50          
    50          
343 0         0 die "Invalid p: $p\n";
344             } elsif ($p == 1){
345 0         0 $x = 0;
346             } elsif ($n == 1) {
347 0         0 $x = _subu($p / 2) ** 2;
348             } elsif ($n == 2) {
349 1         15 $x = -2 * log($p);
350             } else {
351 0         0 my $u = _subu($p);
352 0         0 my $u2 = $u * $u;
353              
354 0         0 $x = max(0, $n + sqrt(2 * $n) * $u
355             + 2/3 * ($u2 - 1)
356             + $u * ($u2 - 7) / 9 / sqrt(2 * $n)
357             - 2/405 / $n * ($u2 * (3 *$u2 + 7) - 16));
358              
359 0 0       0 if ($n <= 100) {
360 0         0 my ($x0, $p1, $z);
361 0   0     0 do {
362 0         0 $x0 = $x;
363 0 0       0 if ($x < 0) {
    0          
    0          
364 0         0 $p1 = 1;
365             } elsif ($n>100) {
366 0         0 $p1 = _subuprob((($x / $n)**(1/3) - (1 - 2/9/$n))
367             / sqrt(2/9/$n));
368             } elsif ($x>400) {
369 0         0 $p1 = 0;
370             } else {
371 0         0 my ($i0, $a);
372 0 0       0 if (($n % 2) != 0) {
373 0         0 $p1 = 2 * _subuprob(sqrt($x));
374 0         0 $a = sqrt(2/PI) * exp(-$x/2) / sqrt($x);
375 0         0 $i0 = 1;
376             } else {
377 0         0 $p1 = $a = exp(-$x/2);
378 0         0 $i0 = 2;
379             }
380              
381 0         0 for (my $i = $i0; $i <= $n-2; $i += 2) {
382 0         0 $a *= $x / $i;
383 0         0 $p1 += $a;
384             }
385             }
386 0         0 $z = exp((($n-1) * log($x/$n) - log(4*PI*$x)
387             + $n - $x - 1/$n/6) / 2);
388 0         0 $x += ($p1 - $p) / $z;
389 0         0 $x = sprintf("%.5f", $x);
390             } while (($n < 31) && (abs($x0 - $x) > 1e-4));
391             }
392             }
393 1         5 return $x;
394             }
395              
396             sub log10 {
397 17     17 0 18 my $n = shift;
398 17         211 return log($n) / log(10);
399             }
400            
401             sub max {
402 8     8 0 12 my $max = shift;
403 8         6 my $next;
404 8         18 while (@_) {
405 8         9 $next = shift;
406 8 50       28 $max = $next if ($next > $max);
407             }
408 8         28 return $max;
409             }
410              
411             sub min {
412 0     0 0 0 my $min = shift;
413 0         0 my $next;
414 0         0 while (@_) {
415 0         0 $next = shift;
416 0 0       0 $min = $next if ($next < $min);
417             }
418 0         0 return $min;
419             }
420              
421             sub precision {
422 8     8 0 8 my ($x) = @_;
423 8         18 return abs int(log10(abs $x) - SIGNIFICANT);
424             }
425              
426             sub precision_string {
427 8     8 0 11 my ($x) = @_;
428 8 50       18 if ($x) {
429 8         15 return sprintf "%." . precision($x) . "f", $x;
430             } else {
431 0           return "0";
432             }
433             }
434              
435              
436             # Autoload methods go after =cut, and are processed by the autosplit program.
437              
438             1;
439             __END__