root/trunk/random.d

Revision 211, 39.3 kB (checked in by dsimcha, 2 weeks ago)

Fix ranges: Add save() and @property stuff.

Line 
1 /**Generates random samples from a various probability distributions.
2  * These are mostly D ports of the NumPy random number generators.*/
3
4 /* This library is a D port of a large portion of the Numpy random number
5  * library.  A few distributions were excluded because they were too obscure
6  * to be tested properly.  They may be included at some point in the future.
7  *
8  * Port to D copyright 2009 David Simcha.
9  *
10  * The original C code is available under the licenses below.  No additional
11  * restrictions shall apply to this D translation.  Eventually, I will try to
12  * discuss the licensing issues with the original authors of Numpy and
13  * make this sane enough that this module can be included in Phobos without
14  * concern.  For now, it's free enough that you can at least use it in
15  * personal projects without any serious issues.
16  *
17  * Main Numpy license:
18  *
19  * Copyright (c) 2005-2009, NumPy Developers.
20  * All rights reserved.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions are
24  * met:
25  *
26  *     * Redistributions of source code must retain the above copyright
27  *        notice, this list of conditions and the following disclaimer.
28  *
29  *     * Redistributions in binary form must reproduce the above
30  *        copyright notice, this list of conditions and the following
31  *        disclaimer in the documentation and/or other materials provided
32  *        with the distribution.
33  *
34  *     * Neither the name of the NumPy Developers nor the names of any
35  *        contributors may be used to endorse or promote products derived
36  *        from this software without specific prior written permission.
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
39  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
40  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
41  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
42  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
44  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
48  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49  *
50  * distribution.c  license:
51  *
52  * Copyright 2005 Robert Kern (robert.kern@gmail.com)
53  *
54  * Permission is hereby granted, free of charge, to any person obtaining a
55  * copy of this software and associated documentation files (the
56  * "Software"), to deal in the Software without restriction, including
57  * without limitation the rights to use, copy, modify, merge, publish,
58  * distribute, sublicense, and/or sell copies of the Software, and to
59  * permit persons to whom the Software is furnished to do so, subject to
60  * the following conditions:
61  *
62  * The above copyright notice and this permission notice shall be included
63  * in all copies or substantial portions of the Software.
64  *
65  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
66  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
67  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
68  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
69  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
70  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
71  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
72  */
73
74 /* The implementations of rHypergeometricHyp() and rHypergeometricHrua()
75  * were adapted from Ivan Frohne's rv.py which has this
76  * license:
77  *
78  *            Copyright 1998 by Ivan Frohne; Wasilla, Alaska, U.S.A.
79  *                            All Rights Reserved
80  *
81  * Permission to use, copy, modify and distribute this software and its
82  * documentation for any purpose, free of charge, is granted subject to the
83  * following conditions:
84  *   The above copyright notice and this permission notice shall be included in
85  *   all copies or substantial portions of the software.
86  *
87  *   THE SOFTWARE AND DOCUMENTATION IS PROVIDED WITHOUT WARRANTY OF ANY KIND,
88  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY, FITNESS
89  *   FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHOR
90  *   OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM OR DAMAGES IN A CONTRACT
91  *   ACTION, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
92  *   SOFTWARE OR ITS DOCUMENTATION.
93  */
94
95  /* References:
96  *
97  * Devroye, Luc. _Non-Uniform Random Variate Generation_.
98  *  Springer-Verlag, New York, 1986.
99  *  http://cgm.cs.mcgill.ca/~luc/rnbookindex.html
100  *
101  * Kachitvichyanukul, V. and Schmeiser, B. W. Binomial Random Variate
102  *  Generation. Communications of the ACM, 31, 2 (February, 1988) 216.
103  *
104  * Hoermann, W. The Transformed Rejection Method for Generating Poisson Random
105  *  Variables. Insurance: Mathematics and Economics, (to appear)
106  *  http://citeseer.csail.mit.edu/151115.html
107  *
108  * Marsaglia, G. and Tsang, W. W. A Simple Method for Generating Gamma
109  * Variables. ACM Transactions on Mathematical Software, Vol. 26, No. 3,
110  * September 2000, Pages 363-372.
111  */
112
113
114 /* Unit tests are non-deterministic.  They prove that the distributions
115  * are reasonable by using K-S tests and summary stats, but cannot
116  * deterministically prove correctness.*/
117
118 module dstats.random;
119
120 import std.math, std.algorithm, dstats.distrib, std.traits, std.typetuple,
121     std.exception;
122 public import std.random; //For uniform distrib.
123
124 import dstats.alloc, dstats.base;
125
126 version(unittest) {
127     import std.stdio, dstats.tests, dstats.summary, dstats.gamma,
128            std.range;
129     void main() {}
130 }
131
132 /**Convenience function to allow one-statement creation of arrays of random
133  * numbers.
134  *
135  * Examples:
136  * ---
137  * // Create an array of 10 random numbers distributed Normal(0, 1).
138  * auto normals = randArray!rNorm(10, 0, 1);
139  * ---
140  */
141 auto randArray(alias randFun, Args...)(size_t N, Args args) {
142     alias typeof(randFun(args)) R;
143     return randArray!(R, randFun, Args)(N, args);
144 }
145
146 unittest {
147     // Just check if it compiles.
148     auto nums = randArray!rNorm(5, 0, 1);
149     auto nums2 = randArray!rBinomial(10, 5, 0.5);
150 }
151
152 /**Allows the creation of an array of random numbers with an explicitly
153  * specified type.  Useful, for example, when single-precision floats are all
154  * you need.
155  *
156  * Examples:
157  * ---
158  * // Create an array of 10 million floats distributed Normal(0, 1).
159  * float[] normals = randArray!(float, rNorm)(10, 0, 1);
160  * ---
161  */
162 R[] randArray(R, alias randFun, Args...)(size_t N, Args args) {
163     auto ret = newVoid!(R)(N);
164     foreach(ref elem; ret) {
165         elem = randFun(args);
166     }
167
168     return ret;
169 }
170
171 ///
172 struct RandRange(alias randFun, T...) {
173 private:
174     T args;
175     double normData = double.nan;  // TLS stuff for normal.
176     typeof(randFun(args)) frontElem;
177 public:
178     enum bool empty = false;
179
180     this(T args) {
181         this.args = args;
182         popFront;
183     }
184
185     @property typeof(randFun(args)) front() {
186         return frontElem;
187     }
188
189     void popFront() {
190         /* This is a kludge to make the contents of this range deterministic
191          * given the state of the underlying random number generator without
192          * a massive redesign.  We store the state in this struct and
193          * swap w/ the TLS data for rNorm on each call to popFront.  This has to
194          * be done no matter what distribution we're using b/c a lot of others
195          * rely on the normal.*/
196         auto lastNormPtr = &lastNorm;  // Cache ptr once, avoid repeated TLS lookup.
197         auto temp = *lastNormPtr;  // Store old state.
198         *lastNormPtr = normData;  // Replace it.
199         this.frontElem = randFun(args);
200         normData = *lastNormPtr;
201         *lastNormPtr = temp;
202     }
203
204     @property typeof(this) save() {
205         return this;
206     }
207 }
208
209 /**Turn a random number generator function into an infinite range.
210  * Params is a tuple of the distribution parameters.  This is specified
211  * in the same order as when calling the function directly.
212  *
213  * The sequence generated by this range is deterministic and repeatable given
214  * the state of the underlying random number generator.  If the underlying
215  * random number generator is explicitly specified, as opposed to using the
216  * default thread-local global RNG, it is copied when the struct is copied.
217  * See below for an example of this behavior.
218  *
219  * Examples:
220  * ---
221  * // Print out some summary statistics for 10,000 Poisson-distributed
222  * // random numbers w/ Poisson parameter 2.
223  * auto gen = Random(unpredictableSeed);
224  * auto pois1k = take(10_000, randRange!rPoisson(2, gen));
225  * writeln( summary(pois1k) );
226  * writeln( summary(pois1k) );  // Exact same results as first call.
227  * ---
228  */
229 RandRange!(randFun, T) randRange(alias randFun, T...)(T params) {
230     alias RandRange!(randFun, T) RT;
231     RT ret;  // Bypass the ctor b/c it's screwy.
232     ret.args = params;
233     ret.popFront;
234     return ret;
235 }
236
237 unittest {
238     // The thing to test here is that the results are deterministic given
239     // an underlying RNG.
240
241     {
242         auto norms = take(randRange!rNorm(0, 1, Random(unpredictableSeed)), 99);
243         auto arr1 = toArray(norms);
244         auto arr2 = toArray(norms);
245         assert(arr1 == arr2);
246     }
247
248     {
249         auto binomSmall = take(randRange!rBinomial(20, 0.5, Random(unpredictableSeed)), 99);
250         auto arr1 = toArray(binomSmall);
251         auto arr2 = toArray(binomSmall);
252         assert(arr1 == arr2);
253     }
254
255     {
256         auto binomLarge = take(randRange!rBinomial(20000, 0.4, Random(unpredictableSeed)), 99);
257         auto arr1 = toArray(binomLarge);
258         auto arr2 = toArray(binomLarge);
259         assert(arr1 == arr2);
260     }
261     writeln("Passed RandRange test.");
262 }
263
264 // Thread local data for normal distrib. that is preserved across calls.
265 private static double lastNorm = double.nan;
266
267 ///
268 double rNorm(RGen = Random)(double mean, double sd, ref RGen gen = rndGen) {
269     dstatsEnforce(sd > 0, "Standard deviation must be > 0 for rNorm.");
270
271     double lr = lastNorm;
272     if (!isNaN(lr)) {
273         lastNorm = double.nan;
274         return lr * sd + mean;
275     }
276
277     double x1 = void, x2 = void, r2 = void;
278     do {
279         x1 = uniform(-1.0L, 1.0L, gen);
280         x2 = uniform(-1.0L, 1.0L, gen);
281         r2 = x1 * x1 + x2 * x2;
282     } while (r2 > 1.0L || r2 == 0.0L);
283     double f = sqrt(-2.0L * log(r2) / r2);
284     lastNorm = f * x1;
285     return f * x2 * sd + mean;
286 }
287
288
289 unittest {
290     auto observ = randArray!rNorm(100_000, 0, 1);
291     auto ksRes = ksTest(observ, parametrize!(normalCDF)(0.0L, 1.0L));
292     auto summ = summary(observ);
293
294     writeln("100k samples from normal(0, 1):  K-S P-val:  ", ksRes.p);
295     writeln("\tMean Expected: 0  Observed:  ", summ.mean);
296     writeln("\tMedian Expected: 0  Observed:  ", median(observ));
297     writeln("\tStdev Expected:  1  Observed:  ", summ.stdev);
298     writeln("\tKurtosis Expected:  0  Observed:  ", summ.kurtosis);
299     writeln("\tSkewness Expected:  0  Observed:  ", summ.skewness);
300 }
301
302 ///
303 double rCauchy(RGen = Random)(double X0, double gamma, ref RGen gen = rndGen) {
304     dstatsEnforce(gamma > 0, "gamma must be > 0 for Cauchy distribution.");
305
306     return (rNorm(0, 1, gen) / rNorm(0, 1, gen)) * gamma + X0;
307 }
308
309 unittest {
310     auto observ = randArray!rCauchy(100_000, 2, 5);
311     auto ksRes = ksTest(observ, parametrize!(cauchyCDF)(2.0L, 5.0L));
312
313     auto summ = summary(observ);
314     writeln("100k samples from Cauchy(2, 5):  K-S P-val:  ", ksRes.p);
315     writeln("\tMean Expected: N/A  Observed:  ", summ.mean);
316     writeln("\tMedian Expected: 2  Observed:  ", median(observ));
317     writeln("\tStdev Expected:  N/A  Observed:  ", summ.stdev);
318     writeln("\tKurtosis Expected:  N/A  Observed:  ", summ.kurtosis);
319     writeln("\tSkewness Expected:  N/A  Observed:  ", summ.skewness);
320 }
321
322 ///
323 double rStudentT(RGen = Random)(double df, ref RGen gen = rndGen) {
324     dstatsEnforce(df > 0, "Student's T distribution must have >0 degrees of freedom.");
325
326     double N = rNorm(0, 1, gen);
327     double G = stdGamma(df / 2, gen);
328     double X = sqrt(df / 2) * N / sqrt(G);
329     return X;
330 }
331
332 unittest {
333     auto observ = randArray!rStudentT(100_000, 5);
334     auto ksRes = ksTest(observ, parametrize!(studentsTCDF)(5));
335
336     auto summ = summary(observ);
337     writeln("100k samples from T(5):  K-S P-val:  ", ksRes.p);
338     writeln("\tMean Expected: 0  Observed:  ", summ.mean);
339     writeln("\tMedian Expected: 0  Observed:  ", median(observ));
340     writeln("\tStdev Expected:  1.2909  Observed:  ", summ.stdev);
341     writeln("\tKurtosis Expected:  6  Observed:  ", summ.kurtosis);
342     writeln("\tSkewness Expected:  0  Observed:  ", summ.skewness);
343 }
344
345 ///
346 double rFisher(RGen = Random)(double df1, double df2, ref RGen gen = rndGen) {
347     dstatsEnforce(df1 > 0 && df2 > 0,
348         "df1 and df2 must be >0 for the Fisher distribution.");
349
350     return (rChiSquare(df1, gen) * df2) /
351            (rChiSquare(df2, gen) * df1);
352 }
353
354 unittest {
355     auto observ = randArray!rFisher(100_000, 5, 7);
356     auto ksRes = ksTest(observ, parametrize!(fisherCDF)(5, 7));
357     writeln("100k samples from fisher(5, 7):  K-S P-val:  ", ksRes.p);
358     writeln("\tMean Expected: ",  7.0 / 5, "  Observed:  ", mean(observ));
359     writeln("\tMedian Expected: ??  Observed:  ", median(observ));
360     writeln("\tStdev Expected:  ??  Observed:  ", stdev(observ));
361     writeln("\tKurtosis Expected:  ??  Observed:  ", kurtosis(observ));
362     writeln("\tSkewness Expected:  ??  Observed:  ", skewness(observ));
363     delete observ;
364 }
365
366 ///
367 double rChiSquare(RGen = Random)(double df, ref RGen gen = rndGen) {
368     dstatsEnforce(df > 0, "df must be > 0 for chiSquare distribution.");
369
370     return 2.0 * stdGamma(df / 2.0L, gen);
371 }
372
373 unittest {
374     double df = 5;
375     double[] observ = new double[100_000];
376     foreach(ref elem; observ)
377     elem = rChiSquare(df);
378     auto ksRes = ksTest(observ, parametrize!(chiSqrCDF)(5));
379     writeln("100k samples from Chi-Square:  K-S P-val:  ", ksRes.p);
380     writeln("\tMean Expected: ", df, "  Observed:  ", mean(observ));
381     writeln("\tMedian Expected: ", df - (2.0L / 3.0L), "  Observed:  ", median(observ));
382     writeln("\tStdev Expected:  ", sqrt(2 * df), "  Observed:  ", stdev(observ));
383     writeln("\tKurtosis Expected:  ", 12 / df, "  Observed:  ", kurtosis(observ));
384     writeln("\tSkewness Expected:  ", sqrt(8 / df), "  Observed:  ", skewness(observ));
385     delete observ;
386 }
387
388 ///
389 int rPoisson(RGen = Random)(double lam, ref RGen gen = rndGen) {
390     dstatsEnforce(lam > 0, "lambda must be >0 for Poisson distribution.");
391
392     static int poissonMult(ref RGen gen, double lam) {
393         double U = void;
394
395         double enlam = exp(-lam);
396         int X = 0;
397         double prod = 1.0;
398         while (true) {
399             U = uniform(0.0L, 1.0L, gen);
400             prod *= U;
401             if (prod > enlam) {
402                 X += 1;
403             } else {
404                 return X;
405             }
406         }
407         assert(0);
408     }
409
410     enum double LS2PI = 0.91893853320467267;
411     enum double TWELFTH = 0.083333333333333333333333;
412     static int poissonPtrs(ref RGen gen, double lam) {
413         int k;
414         double U = void, V = void, us = void;
415
416         double slam = sqrt(lam);
417         double loglam = log(lam);
418         double b = 0.931 + 2.53*slam;
419         double a = -0.059 + 0.02483*b;
420         double invalpha = 1.1239 + 1.1328/(b-3.4);
421         double vr = 0.9277 - 3.6224/(b-2);
422
423         while (true) {
424             U = uniform(-0.5L, 0.5L, gen);
425             V = uniform(0.0L, 1.0L, gen);
426             us = 0.5 - abs(U);
427             k = cast(int) floor((2*a/us + b)*U + lam + 0.43);
428             if ((us >= 0.07) && (V <= vr)) {
429                 return k;
430             }
431             if ((k < 0) || ((us < 0.013) && (V > us))) {
432                 continue;
433             }
434             if ((log(V) + log(invalpha) - log(a/(us*us)+b)) <=
435                     (-lam + k*loglam - lgamma(k+1))) {
436                 return k;
437             }
438         }
439         assert(0);
440     }
441
442
443     if (lam >= 10) {
444         return poissonPtrs(gen, lam);
445     } else if (lam == 0) {
446         return 0;
447     } else {
448         return poissonMult(gen, lam);
449     }
450 }
451
452 unittest {
453     double lambda = 15L;
454     int[] observ = new int[100_000];
455     foreach(ref elem; observ)
456     elem = rPoisson(lambda);
457     writeln("100k samples from poisson(", lambda, "):");
458     writeln("\tMean Expected: ", lambda,
459             "  Observed:  ", mean(observ));
460     writeln("\tMedian Expected: ??  Observed:  ", median(observ));
461     writeln("\tStdev Expected:  ", sqrt(lambda),
462             "  Observed:  ", stdev(observ));
463     writeln("\tKurtosis Expected:  ", 1 / lambda,
464             "  Observed:  ", kurtosis(observ));
465     writeln("\tSkewness Expected:  ", 1 / sqrt(lambda),
466             "  Observed:  ", skewness(observ));
467     delete observ;
468 }
469
470 ///
471 int rBernoulli(RGen = Random)(double P = 0.5, ref RGen gen = rndGen) {
472     dstatsEnforce(P >= 0 && P <= 1, "P must be between 0, 1 for Bernoulli distribution.");
473
474     double pVal = uniform(0.0L, 1.0L, gen);
475     return cast(int) (pVal <= P);
476 }
477
478 private struct BinoState {
479     bool has_binomial;
480     int nsave;
481     double psave;
482     int m;
483     double r,q,fm,p1,xm,xl,xr,c,laml,lamr,p2,p3,p4;
484     double a,u,v,s,F,rho,t,A,nrq,x1,x2,f1,f2,z,z2,w,w2,x;
485 }
486
487 private BinoState* binoState() {
488     // Store BinoState structs on heap rather than directly in TLS.
489
490     static BinoState* stateTLS;
491     auto tlsPtr = stateTLS;
492     if (tlsPtr is null) {
493         tlsPtr = new BinoState;
494         stateTLS = tlsPtr;
495     }
496     return tlsPtr;
497 }
498
499
500 private int rBinomialBtpe(RGen = Random)(int n, double p, ref RGen gen = rndGen) {
501     auto state = binoState;
502     double r,q,fm,p1,xm,xl,xr,c,laml,lamr,p2,p3,p4;
503     double a,u,v,s,F,rho,t,A,nrq,x1,x2,f1,f2,z,z2,w,w2,x;
504     int m,y,k,i;
505
506     if (!(state.has_binomial) ||
507             (state.nsave != n) ||
508             (state.psave != p)) {
509         /* initialize */
510         state.nsave = n;
511         state.psave = p;
512         state.has_binomial = 1;
513         state.r = r = min(p, 1.0-p);
514         state.q = q = 1.0 - r;
515         state.fm = fm = n*r+r;
516         state.m = m = cast(int)floor(state.fm);
517         state.p1 = p1 = floor(2.195*sqrt(n*r*q)-4.6*q) + 0.5;
518         state.xm = xm = m + 0.5;
519         state.xl = xl = xm - p1;
520         state.xr = xr = xm + p1;
521         state.c = c = 0.134 + 20.5/(15.3 + m);
522         a = (fm - xl)/(fm-xl*r);
523         state.laml = laml = a*(1.0 + a/2.0);
524         a = (xr - fm)/(xr*q);
525         state.lamr = lamr = a*(1.0 + a/2.0);
526         state.p2 = p2 = p1*(1.0 + 2.0*c);
527         state.p3 = p3 = p2 + c/laml;
528         state.p4 = p4 = p3 + c/lamr;
529     } else {
530         r = state.r;
531         q = state.q;
532         fm = state.fm;
533         m = state.m;
534         p1 = state.p1;
535         xm = state.xm;
536         xl = state.xl;
537         xr = state.xr;
538         c = state.c;
539         laml = state.laml;
540         lamr = state.lamr;
541         p2 = state.p2;
542         p3 = state.p3;
543         p4 = state.p4;
544     }
545
546     /* sigh ... */
547 Step10:
548     nrq = n*r*q;
549     u = uniform(0.0L, p4, gen);
550     v = uniform(0.0L, 1.0L, gen);
551     if (u > p1) goto Step20;
552     y = cast(int)floor(xm - p1*v + u);
553     goto Step60;
554
555 Step20:
556     if (u > p2) goto Step30;
557     x = xl + (u - p1)/c;
558     v = v*c + 1.0 - fabs(m - x + 0.5)/p1;
559     if (v > 1.0) goto Step10;
560     y = cast(int)floor(x);
561     goto Step50;
562
563 Step30:
564     if (u > p3) goto Step40;
565     y = cast(int)floor(xl + log(v)/laml);
566     if (y < 0) goto Step10;
567     v = v*(u-p2)*laml;
568     goto Step50;
569
570 Step40:
571     y = cast(int)floor(xr - log(v)/lamr);
572     if (y > n) goto Step10;
573     v = v*(u-p3)*lamr;
574
575 Step50:
576     k = cast(int) fabs(y - m);
577     if ((k > 20) && (k < ((nrq)/2.0 - 1))) goto Step52;
578
579     s = r/q;
580     a = s*(n+1);
581     F = 1.0;
582     if (m < y) {
583         for (i=m; i<=y; i++) {
584             F *= (a/i - s);
585         }
586     } else if (m > y) {
587         for (i=y; i<=m; i++) {
588             F /= (a/i - s);
589         }
590     } else {
591         if (v > F) goto Step10;
592         goto Step60;
593     }
594
595 Step52:
596     rho = (k/(nrq))*((k*(k/3.0 + 0.625) + 0.16666666666666666)/nrq + 0.5);
597     t = -k*k/(2*nrq);
598     A = log(v);
599     if (A < (t - rho)) goto Step60;
600     if (A > (t + rho)) goto Step10;
601
602     x1 = y+1;
603     f1 = m+1;
604     z = n+1-m;
605     w = n-y+1;
606     x2 = x1*x1;
607     f2 = f1*f1;
608     z2 = z*z;
609     w2 = w*w;
610     if (A > (xm*log(f1/x1)
611              + (n-m+0.5)*log(z/w)
612              + (y-m)*log(w*r/(x1*q))
613              + (13680.-(462.-(132.-(99.-140./f2)/f2)/f2)/f2)/f1/166320.
614              + (13680.-(462.-(132.-(99.-140./z2)/z2)/z2)/z2)/z/166320.
615              + (13680.-(462.-(132.-(99.-140./x2)/x2)/x2)/x2)/x1/166320.
616              + (13680.-(462.-(132.-(99.-140./w2)/w2)/w2)/w2)/w/166320.)) {
617         goto Step10;
618     }
619
620 Step60:
621     if (p > 0.5) {
622         y = n - y;
623     }
624
625     return y;
626 }
627
628 private int rBinomialInversion(RGen = Random)(int n, double p, ref RGen gen = rndGen) {
629     auto state = binoState;
630     double q, qn, np, px, U;
631     int X, bound;
632
633     if (!(state.has_binomial) ||
634             (state.nsave != n) ||
635             (state.psave != p)) {
636         state.nsave = n;
637         state.psave = p;
638         state.has_binomial = 1;
639         state.q = q = 1.0 - p;
640         state.r = qn = exp(n * log(q));
641         state.c = np = n*p;
642         state.m = bound = cast(int) min(n, np + 10.0*sqrt(np*q + 1));
643     } else {
644         q = state.q;
645         qn = state.r;
646         np = state.c;
647         bound = cast(int) state.m;
648     }
649     X = 0;
650     px = qn;
651     U = uniform(0.0L, 1.0L, gen);
652     while (U > px) {
653         X++;
654         if (X > bound) {
655             X = 0;
656             px = qn;
657             U = uniform(0.0L, 1.0L, gen);
658         } else {
659             U -= px;
660             px  = ((n-X+1) * p * px)/(X*q);
661         }
662     }
663     return X;
664 }
665
666 ///
667 int rBinomial(RGen = Random)(int n, double p, ref RGen gen = rndGen) {
668     dstatsEnforce(n >= 0, "n must be >= 0 for binomial distribution.");
669     dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 for binomial distribution.");
670
671     if (p <= 0.5) {
672         if (p*n <= 30.0) {
673             return rBinomialInversion(n, p, gen);
674         } else {
675             return rBinomialBtpe(n, p, gen);
676         }
677     } else {
678         double q = 1.0-p;
679         if (q*n <= 30.0) {
680             return n - rBinomialInversion(n, q, gen);
681         } else {
682             return n - rBinomialBtpe(n, q, gen);
683         }
684     }
685 }
686
687 unittest {
688     void testBinom(int n, double p) {
689         int[] observ = new int[100_000];
690         foreach(ref elem; observ)
691         elem = rBinomial(n, p);
692         writeln("100k samples from binom.(", n, ", ", p, "):");
693         writeln("\tMean Expected: ", n * p,
694                 "  Observed:  ", mean(observ));
695         writeln("\tMedian Expected: ", n * p, "  Observed:  ", median(observ));
696         writeln("\tStdev Expected:  ", sqrt(n * p * (1 - p)),
697                 "  Observed:  ", stdev(observ));
698         writeln("\tKurtosis Expected:  ", (1 - 6 * p * (1 - p)) / (n * p * (1 - p)),
699                 "  Observed:  ", kurtosis(observ));
700         writeln("\tSkewness Expected:  ", (1 - 2 * p) / (sqrt(n * p * (1 - p))),
701                 "  Observed:  ", skewness(observ));
702         delete observ;
703     }
704
705     testBinom(1000, 0.6);
706     testBinom(3, 0.7);
707 }
708
709 private int hypergeoHyp(RGen = Random)(int good, int bad, int sample, ref RGen gen = rndGen) {
710     int Z = void;
711     double U = void;
712
713     int d1 = bad + good - sample;
714     double d2 = cast(double)min(bad, good);
715
716     double Y = d2;
717     int K = sample;
718     while (Y > 0.0) {
719         U = uniform(0.0L, 1.0L, gen);
720         Y -= cast(int)floor(U + Y/(d1 + K));
721         K--;
722         if (K == 0) break;
723     }
724     Z = cast(int)(d2 - Y);
725     if (good > bad) Z = sample - Z;
726     return Z;
727 }
728
729 private enum double D1 = 1.7155277699214135;
730 private enum double D2 = 0.8989161620588988;
731 private int hypergeoHrua(RGen = Random)(int good, int bad, int sample, ref RGen gen = rndGen) {
732     int Z = void;
733     double T = void, W = void, X = void, Y = void;
734
735     int mingoodbad = min(good, bad);
736     int popsize = good + bad;
737     int maxgoodbad = max(good, bad);
738     int m = min(sample, popsize - sample);
739     double d4 = (cast(double)mingoodbad) / popsize;
740     double d5 = 1.0 - d4;
741     double d6 = m*d4 + 0.5;
742     double d7 = sqrt((popsize - m) * sample * d4 *d5 / (popsize-1) + 0.5);
743     double d8 = D1*d7 + D2;
744     int d9 = cast(int)floor(cast(double)((m+1)*(mingoodbad+1))/(popsize+2));
745     double d10 = (lgamma(d9+1) + lgamma(mingoodbad-d9+1) + lgamma(m-d9+1) +
746                 lgamma(maxgoodbad-m+d9+1));
747     double d11 = min(min(m, mingoodbad)+1.0, floor(d6+16*d7));
748     /* 16 for 16-decimal-digit precision in D1 and D2 */
749
750     while (true) {
751         X = uniform(0.0L, 1.0L, gen);
752         Y = uniform(0.0L, 1.0L, gen);
753         W = d6 + d8*(Y- 0.5)/X;
754
755         /* fast rejection: */
756         if ((W < 0.0) || (W >= d11)) continue;
757
758         Z = cast(int)floor(W);
759         T = d10 - (lgamma(Z+1) + lgamma(mingoodbad-Z+1) + lgamma(m-Z+1) +
760                    lgamma(maxgoodbad-m+Z+1));
761
762         /* fast acceptance: */
763         if ((X*(4.0-X)-3.0) <= T) break;
764
765         /* fast rejection: */
766         if (X*(X-T) >= 1) continue;
767
768         if (2.0*log(X) <= T) break;  /* acceptance */
769     }
770
771     /* this is a correction to HRUA* by Ivan Frohne in rv.py */
772     if (good > bad) Z = m - Z;
773
774     /* another fix from rv.py to allow sample to exceed popsize/2 */
775     if (m < sample) Z = good - Z;
776
777     return Z;
778 }
779
780 ///
781 int rHypergeometric(RGen = Random)(int n1, int n2, int n, ref RGen gen = rndGen) {
782     dstatsEnforce(n <= n1 + n2, "n must be <= n1 + n2 for hypergeometric distribution.");
783     dstatsEnforce(n1 >= 0 && n2 >= 0 && n >= 0,
784         "n, n1, n2 must be >= 0 for hypergeometric distribution.");
785
786     alias n1 good;
787     alias n2 bad;
788     alias n sample;
789     if (sample > 10) {
790         return hypergeoHrua(good, bad, sample, gen);
791     } else {
792         return hypergeoHyp(good, bad, sample, gen);
793     }
794 }
795
796 unittest {
797
798     static double hyperStdev(int n1, int n2, int n) {
799         return sqrt(cast(double) n * (cast(double) n1 / (n1 + n2))
800         * (1 - cast(double) n1 / (n1 + n2)) * (n1 + n2 - n) / (n1 + n2 - 1));
801     }
802
803     static double hyperSkew(double n1, double n2, double n) {
804         double N = n1 + n2;
805         alias n1 m;
806         double numer = (N - 2 * m) * sqrt(N - 1) * (N - 2 * n);
807         double denom = sqrt(n * m * (N - m) * (N - n)) * (N - 2);
808         return numer / denom;
809     }
810
811     void testHyper(int n1, int n2, int n) {
812         int[] observ = new int[100_000];
813         foreach(ref elem; observ)
814         elem = rHypergeometric(n1, n2, n);
815         auto ksRes = ksTest(observ, parametrize!(hypergeometricCDF)(n1, n2, n));
816         writeln("100k samples from hypergeom.(", n1, ", ", n2, ", ", n, "):");
817         writeln("\tMean Expected: ", n * cast(double) n1 / (n1 + n2),
818                 "  Observed:  ", mean(observ));
819         writeln("\tMedian Expected: ??  Observed:  ", median(observ));
820         writeln("\tStdev Expected:  ", hyperStdev(n1, n2, n),
821                 "  Observed:  ", stdev(observ));
822         writeln("\tKurtosis Expected:  ?? Observed:  ", kurtosis(observ));
823         writeln("\tSkewness Expected:  ", hyperSkew(n1, n2, n), "  Observed:  ", skewness(observ));
824         delete observ;
825     }
826
827     testHyper(4, 5, 2);
828     testHyper(120, 105, 70);
829 }
830
831 private int rGeomSearch(RGen = Random)(double p, ref RGen gen = rndGen) {
832     int X = 1;
833     double sum = p, prod = p;
834     double q = 1.0 - p;
835     double U = uniform(0.0L, 1.0L, gen);
836     while (U > sum) {
837         prod *= q;
838         sum += prod;
839         X++;
840     }
841     return X;
842 }
843
844 private int rGeomInvers(RGen = Random)(double p, ref RGen gen = rndGen) {
845     return cast(int)ceil(log(1.0-uniform(0.0L, 1.0L, gen))/log(1.0-p));
846 }
847
848 int rGeometric(RGen = Random)(double p, ref RGen gen = rndGen) {
849     dstatsEnforce(p >= 0 && p <= 1, "p must be between 0, 1 for geometric distribution.");
850
851     if (p >= 0.333333333333333333333333) {
852         return rGeomSearch(p, gen);
853     } else {
854         return rGeomInvers(p, gen);
855     }
856 }
857
858 unittest {
859
860     void testGeom(double p) {
861         int[] observ = new int[100_000];
862         foreach(ref elem; observ)
863         elem = rGeometric(p);
864         writeln("100k samples from geometric.(", p, "):");
865         writeln("\tMean Expected: ", 1 / p,
866                 "  Observed:  ", mean(observ));
867         writeln("\tMedian Expected: ", ceil(-log(2) / log(1 - p)),
868                 " Observed:  ", median(observ));
869         writeln("\tStdev Expected:  ", sqrt((1 - p) / (p * p)),
870                 "  Observed:  ", stdev(observ));
871         writeln("\tKurtosis Expected:  ", 6 + (p * p) / (1 - p),
872                 "  Observed:  ", kurtosis(observ));
873         writeln("\tSkewness Expected:  ", (2 - p) / sqrt(1 - p),
874                 "  Observed:  ", skewness(observ));
875         delete observ;
876     }
877
878     testGeom(0.1);
879     testGeom(0.74);
880
881 }
882
883 ///
884 int rNegBinom(RGen = Random)(double n, double p, ref RGen gen = rndGen) {
885     dstatsEnforce(n >= 0, "n must be >= 0 for negative binomial distribution.");
886     dstatsEnforce(p >= 0 && p <= 1,
887         "p must be between 0, 1 for negative binomial distribution.");
888
889     double Y = stdGamma(n, gen);
890     Y *= (1 - p) / p;
891     return rPoisson(Y, gen);
892 }
893
894 unittest {
895     Random gen;
896     gen.seed(unpredictableSeed);
897     double p = 0.3L;
898     int n = 30;
899     int[] observ = new int[100_000];
900     foreach(ref elem; observ)
901     elem = rNegBinom(n, p);
902     writeln("100k samples from neg. binom.(", n,", ",  p, "):");
903     writeln("\tMean Expected: ", n * (1 - p) / p,
904             "  Observed:  ", mean(observ));
905     writeln("\tMedian Expected: ??  Observed:  ", median(observ));
906     writeln("\tStdev Expected:  ", sqrt(n * (1 - p) / (p * p)),
907             "  Observed:  ", stdev(observ));
908     writeln("\tKurtosis Expected:  ", (6 - p * (6 - p)) / (n * (1 - p)),
909             "  Observed:  ", kurtosis(observ));
910     writeln("\tSkewness Expected:  ", (2 - p) / sqrt(n * (1 - p)),
911             "  Observed:  ", skewness(observ));
912     delete observ;
913 }
914
915 ///
916 double rLaplace(RGen = Random)(double mu = 0, double b = 1, ref RGen gen = rndGen) {
917     dstatsEnforce(b > 0, "b must be > 0 for Laplace distribution.");
918
919     double p = uniform(0.0L, 1.0L, gen);
920     return invLaplaceCDF(p, mu, b);
921 }
922
923 unittest {
924     Random gen;
925     gen.seed(unpredictableSeed);
926     double[] observ = new double[100_000];
927     foreach(ref elem; observ)
928     elem = rLaplace();
929     auto ksRes = ksTest(observ, parametrize!(laplaceCDF)(0.0L, 1.0L));
930     writeln("100k samples from Laplace(0, 1):  K-S P-val:  ", ksRes.p);
931     writeln("\tMean Expected: 0  Observed:  ", mean(observ));
932     writeln("\tMedian Expected: 0  Observed:  ", median(observ));
933     writeln("\tStdev Expected:  1.414  Observed:  ", stdev(observ));
934     writeln("\tKurtosis Expected:  3  Observed:  ", kurtosis(observ));
935     writeln("\tSkewness Expected:  0  Observed:  ", skewness(observ));
936     delete observ;
937 }
938
939 ///
940 double rExponential(RGen = Random)(double lambda, ref RGen gen = rndGen) {
941     dstatsEnforce(lambda > 0, "lambda must be > 0 for exponential distribution.");
942
943     double p = uniform(0.0L, 1.0L, gen);
944     return -log(p) / lambda;
945 }
946
947 unittest {
948     double[] observ = new double[100_000];
949     foreach(ref elem; observ)
950     elem = rExponential(2.0L);
951     auto ksRes = ksTest(observ, parametrize!(gammaCDF)(2, 1));
952     writeln("100k samples from exponential(2):  K-S P-val:  ", ksRes.p);
953     writeln("\tMean Expected: 0.5  Observed:  ", mean(observ));
954     writeln("\tMedian Expected: 0.3465  Observed:  ", median(observ));
955     writeln("\tStdev Expected:  0.5  Observed:  ", stdev(observ));
956     writeln("\tKurtosis Expected:  6  Observed:  ", kurtosis(observ));
957     writeln("\tSkewness Expected:  2  Observed:  ", skewness(observ));
958     delete observ;
959 }
960
961 private double stdGamma(RGen = Random)(double shape, ref RGen gen) {
962     double b = void, c = void;
963     double U = void, V = void, X = void, Y = void;
964
965     if (shape == 1.0) {
966         return rExponential(1.0, gen);
967     } else if (shape < 1.0) {
968         for (;;) {
969             U = uniform(0.0L, 1.0, gen);
970             V = rExponential(1.0, gen);
971             if (U <= 1.0 - shape) {
972                 X = pow(U, 1.0/shape);
973                 if (X <= V) {
974                     return X;
975                 }
976             } else {
977                 Y = -log((1-U)/shape);
978                 X = pow(1.0 - shape + shape*Y, 1./shape);
979                 if (X <= (V + Y)) {
980                     return X;
981                 }
982             }
983         }
984     } else {
985         b = shape - 1./3.;
986         c = 1./sqrt(9*b);
987         for (;;) {
988             do {
989                 X = rNorm(0.0L, 1.0L, gen);
990                 V = 1.0 + c*X;
991             } while (V <= 0.0);
992
993             V = V*V*V;
994             U = uniform(0.0L, 1.0L, gen);
995             if (U < 1.0 - 0.0331*(X*X)*(X*X)) return (b*V);
996             if (log(U) < 0.5*X*X + b*(1. - V + log(V))) return (b*V);
997         }
998     }
999 }
1000
1001 ///
1002 double rGamma(RGen = Random)(double a, double b, ref RGen gen = rndGen) {
1003     dstatsEnforce(a > 0, "a must be > 0 for gamma distribution.");
1004     dstatsEnforce(b > 0, "b must be > 0 for gamma distribution.");
1005
1006     return stdGamma(b, gen) / a;
1007 }
1008
1009 unittest {
1010     double[] observ = new double[100_000];
1011     foreach(ref elem; observ)
1012     elem = rGamma(2.0L, 3.0L);
1013     auto ksRes = ksTest(observ, parametrize!(gammaCDF)(2, 3));
1014     writeln("100k samples from gamma(2, 3):  K-S P-val:  ", ksRes.p);
1015     writeln("\tMean Expected: 1.5  Observed:  ", mean(observ));
1016     writeln("\tMedian Expected: ??  Observed:  ", median(observ));
1017     writeln("\tStdev Expected:  0.866  Observed:  ", stdev(observ));
1018     writeln("\tKurtosis Expected:  2  Observed:  ", kurtosis(observ));
1019     writeln("\tSkewness Expected:  1.15  Observed:  ", skewness(observ));
1020     delete observ;
1021 }
1022
1023 ///
1024 double rBeta(RGen = Random)(double a, double b, ref RGen gen = rndGen) {
1025     dstatsEnforce(a > 0, "a must be > 0 for beta distribution.");
1026     dstatsEnforce(b > 0, "b must be > 0 for beta distribution.");
1027
1028     double Ga = void, Gb = void;
1029
1030     if ((a <= 1.0) && (b <= 1.0)) {
1031         double U, V, X, Y;
1032         /* Use Jonk's algorithm */
1033
1034         while (1) {
1035             U = uniform(0.0L, 1.0L, gen);
1036             V = uniform(0.0L, 1.0L, gen);
1037             X = pow(U, 1.0/a);
1038             Y = pow(V, 1.0/b);
1039
1040             if ((X + Y) <= 1.0) {
1041                 return X / (X + Y);
1042             }
1043         }
1044     } else {
1045         Ga = stdGamma(a, gen);
1046         Gb = stdGamma(b, gen);
1047         return Ga/(Ga + Gb);
1048     }
1049     assert(0);
1050 }
1051
1052 unittest {
1053     double delegate(double) paramBeta(double a, double b) {
1054         double parametrizedBeta(double x) {
1055             return betaIncomplete(a, b, x);
1056         }
1057         return &parametrizedBeta;
1058     }
1059
1060     static double betaStdev(double a, double b) {
1061         return sqrt(a * b / ((a + b) * (a + b) * (a + b + 1)));
1062     }
1063
1064     static double betaSkew(double a, double b) {
1065         auto numer = 2 * (b - a) * sqrt(a + b + 1);
1066         auto denom = (a + b + 2) * sqrt(a * b);
1067         return numer / denom;
1068     }
1069
1070     static double betaKurtosis(double a, double b) {
1071         double numer = a * a * a - a * a * (2 * b - 1) + b * b * (b + 1) - 2 * a * b * (b + 2);
1072         double denom = a * b * (a + b + 2) * (a + b + 3);
1073         return 6 * numer / denom;
1074     }
1075
1076     void testBeta(double a, double b) {
1077         double[] observ = new double[100_000];
1078         foreach(ref elem; observ)
1079         elem = rBeta(a, b);
1080         auto ksRes = ksTest(observ, paramBeta(a, b));
1081         auto summ = summary(observ);
1082         writeln("100k samples from beta(", a, ", ", b, "):  K-S P-val:  ", ksRes.p);
1083         writeln("\tMean Expected: ", a / (a + b), " Observed:  ", summ.mean);
1084         writeln("\tMedian Expected: ??  Observed:  ", median(observ));
1085         writeln("\tStdev Expected:  ", betaStdev(a, b), "  Observed:  ", summ.stdev);
1086         writeln("\tKurtosis Expected:  ", betaKurtosis(a, b), "  Observed:  ", summ.kurtosis);
1087         writeln("\tSkewness Expected:  ", betaSkew(a, b), "  Observed:  ", summ.skewness);
1088         delete observ;
1089     }
1090
1091     testBeta(0.5, 0.7);
1092     testBeta(5, 3);
1093 }
1094
1095 ///
1096 double rLogistic(RGen = Random)(double loc, double scale, ref RGen gen = rndGen) {
1097     dstatsEnforce(scale > 0, "scale must be > 0 for logistic distribution.");
1098
1099     double U = uniform(0.0L, 1.0L, gen);
1100     return loc + scale * log(U/(1.0 - U));
1101 }
1102
1103 unittest {
1104     double[] observ = new double[100_000];
1105     foreach(ref elem; observ)
1106     elem = rLogistic(2.0L, 3.0L);
1107     auto ksRes = ksTest(observ, parametrize!(logisticCDF)(2, 3));
1108     writeln("100k samples from logistic(2, 3):  K-S P-val:  ", ksRes.p);
1109     writeln("\tMean Expected: 2  Observed:  ", mean(observ));
1110     writeln("\tMedian Expected: 2  Observed:  ", median(observ));
1111     writeln("\tStdev Expected:  ", PI * PI * 3, " Observed:  ", stdev(observ));
1112     writeln("\tKurtosis Expected:  1.2  Observed:  ", kurtosis(observ));
1113     writeln("\tSkewness Expected:  0  Observed:  ", skewness(observ));
1114     delete observ;
1115 }
1116
1117 ///
1118 double rLogNorm(RGen = Random)(double mu, double sigma, ref RGen gen = rndGen) {
1119     dstatsEnforce(sigma > 0, "sigma must be > 0 for log-normal distribution.");
1120
1121     return exp(rNorm(mu, sigma, gen));
1122 }
1123
1124 unittest {
1125     auto observ = randArray!rLogNorm(100_000, -2, 1);
1126     auto ksRes = ksTest(observ, paramFunctor!(logNormalCDF)(-2, 1));
1127
1128     auto summ = summary(observ);
1129     writeln("100k samples from log-normal(-2, 1):  K-S P-val:  ", ksRes.p);
1130     writeln("\tMean Expected: ", exp(-1.5), "  Observed:  ", summ.mean);
1131     writeln("\tMedian Expected: ", exp(-2.0L), "  Observed:  ", median(observ));
1132     writeln("\tStdev Expected:  ", sqrt((exp(1.) - 1) * exp(-4.0L + 1)),
1133             " Observed:  ", summ.stdev);
1134     writeln("\tKurtosis Expected:  ?? Observed:  ", summ.kurtosis);
1135     writeln("\tSkewness Expected:  ", (exp(1.) + 2) * sqrt(exp(1.) - 1),
1136             " Observed:  ", summ.skewness);
1137 }
1138
1139 ///
1140 double rWeibull(RGen = Random)(double shape, double scale = 1, ref RGen gen = rndGen) {
1141     dstatsEnforce(shape > 0, "shape must be > 0 for weibull distribution.");
1142     dstatsEnforce(scale > 0, "scale must be > 0 for weibull distribution.");
1143
1144     return pow(rExponential(1, gen), 1. / shape) * scale;
1145 }
1146
1147 unittest {
1148     double[] observ = new double[100_000];
1149     foreach(ref elem; observ)
1150     elem = rWeibull(2.0L, 3.0L);
1151     auto ksRes = ksTest(observ, parametrize!(weibullCDF)(2.0, 3.0));
1152     writeln("100k samples from weibull(2, 3):  K-S P-val:  ", ksRes.p);
1153     delete observ;
1154 }
1155
1156 ///
1157 double rWald(RGen = Random)(double mu, double lambda, ref RGen gen = rndGen) {
1158     dstatsEnforce(mu > 0, "mu must be > 0 for Wald distribution.");
1159     dstatsEnforce(lambda > 0, "lambda must be > 0 for Wald distribution.");
1160
1161     alias mu mean;
1162     alias lambda scale;
1163
1164     double mu_2l = mean / (2*scale);
1165     double Y = rNorm(0, 1, gen);
1166     Y = mean*Y*Y;
1167     double X = mean + mu_2l*(Y - sqrt(4*scale*Y + Y*Y));
1168     double U = uniform(0.0L, 1.0L, gen);
1169     if (U <= mean/(mean+X)) {
1170         return X;
1171     } else
1172
1173     {
1174         return mean*mean/X;
1175     }
1176 }
1177
1178 unittest {
1179     auto observ = randArray!rWald(100_000, 4, 7);
1180     auto ksRes = ksTest(observ, parametrize!(waldCDF)(4, 7));
1181
1182     auto summ = summary(observ);
1183     writeln("100k samples from wald(4, 7):  K-S P-val:  ", ksRes.p);
1184     writeln("\tMean Expected: ", 4, "  Observed:  ", summ.mean);
1185     writeln("\tMedian Expected: ??  Observed:  ", median(observ));
1186     writeln("\tStdev Expected:  ", sqrt(64.0 / 7), " Observed:  ", summ.stdev);
1187     writeln("\tKurtosis Expected:  ", 15.0 * 4 / 7, " Observed:  ", summ.kurtosis);
1188     writeln("\tSkewness Expected:  ", 3 * sqrt(4.0 / 7), " Observed:  ", summ.skewness);
1189 }
1190
1191 ///
1192 double rRayleigh(RGen = Random)(double mode, ref RGen gen = rndGen) {
1193     dstatsEnforce(mode > 0, "mode must be > 0 for Rayleigh distribution.");
1194
1195     return mode*sqrt(-2.0 * log(1.0 - uniform(0.0L, 1.0L, gen)));
1196 }
1197
1198 unittest {
1199     auto observ = randArray!rRayleigh(100_000, 3);
1200     auto ksRes = ksTest(observ, parametrize!(rayleighCDF)(3));
1201     writeln("100k samples from rayleigh(3):  K-S P-val:  ", ksRes.p);
1202 }
Note: See TracBrowser for help on using the browser.