1 /++
2 Random number generators
3 +/
4 /**
5 Authors: [Ilya Yaroshenko](http://9il.github.io)
6 
7 Copyright: © 2014-2015 [Ilya Yaroshenko](http://9il.github.io)
8 
9 License: MIT
10 */
11 module atmosphere.random;
12 
13 import core.stdc.tgmath;
14 
15 import std.math : isNormal, isFinite;
16 import std.random;
17 import std.traits;
18 import atmosphere.params;
19 
20 /++
21 Interface for infinity input range of random numbers.
22 +/
23 interface DistributionRNG(T)
24 {
25 	///always false
26 	enum empty = false;
27 	///do nothing
28 	static void popFront() @safe pure nothrow @nogc {}
29 	/++
30 	Returns: new random number.
31 	+/
32 	T front() @property;
33 }
34 
35 ///
36 unittest
37 {
38 	import std.algorithm : map;
39 	class NormalRNG : DistributionRNG!double
40 	{
41 		double front() @property
42 		{
43 			return rNormal();
44 		}
45 	}
46 
47 	import std.range;
48 	auto rng = new NormalRNG;
49 	auto sigma = 4.0;
50 	auto mu = 2.0;
51 	auto sample = rng.map!(x => x * sigma + mu).take(9).array;
52 }
53 
54 
55 ///
56 DistributionRNG!F toDistributionRNG
57 	(Rng, F = ReturnType!(Rng.front))
58 	(Rng rng)
59 {
60 	static assert(isFloatingPoint!F);
61 	return new class (rng) DistributionRNG!F {
62 		Rng rng;
63 		this(Rng rng) { this.rng = rng; }
64 		F front() @property { return rng.front; };
65 	};
66 
67 }
68 
69 ///
70 unittest
71 {
72 	DistributionRNG!double rng = GammaSRNG!double(rndGen, 1, 3).toDistributionRNG;
73 }
74 
75 
76 /++
77 Class to create normal variance-mean mixture random number generators.
78 Assume `U` has mixing probability density, `Y ~ N(0, 1)`.
79 Class constructs `RNG` for `Z = Y*U^(1/2)+beta*U`.
80 +/
81 class NormalVarianceMeanMixtureRNG(T, UniformRNG = Random) : DistributionRNG!T
82 	if (isFloatingPoint!T)
83 {
84 	private UniformRNG* rng;
85 	
86 	private DistributionRNG!T components;
87 	private T beta;
88 
89 	/++
90 	Constructor
91 	Params:
92 		rng = uniform random number generator (`Y`)
93 		components = mixing random generator (`U`)
94 		beta = mixture scale parameter: `Y*U^(1/2)+beta*U`
95 	+/
96 	this(ref UniformRNG rng, DistributionRNG!T components, T beta)
97 	in {
98 		assert(beta.isFinite);
99 	}
100 	body {
101 		this.rng = &rng;
102 		this.components = components;
103 		this.beta = beta;
104 	}
105 
106 	final T front() @property
107 	{
108 		immutable y = rng.rNormal!T;
109 		immutable u = components.front;
110 		return y * u.sqrt + beta * u;
111 	}
112 }
113 
114 ///
115 unittest
116 {
117 	import std.random;
118 	import std.range;
119 	class MyVarianceGammaRNG : NormalVarianceMeanMixtureRNG!double
120 	{
121 		this(double shape, double scale, double beta)
122 		{
123 			auto components = GammaSRNG!double(rndGen, shape, scale);
124 			super(rndGen, components.toDistributionRNG, beta);
125 		}
126 	}
127 	auto rng = new MyVarianceGammaRNG(1.2, 10, 3);
128 	auto sample = rng.take(10).array;
129 }
130 
131 
132 /++
133 Class to generate random observations from a
134 gamma
135 distribution.
136  +/
137 struct GammaSRNG(T, UniformRNG = Random)
138 	if (isFloatingPoint!T)
139 {
140 	private UniformRNG* rng;
141 	
142 	private T shape, scale;
143 
144 	/++
145 	Constructor
146 	Params:
147 		rng = uniform random number generator
148 		shape = shape parameter
149 		scale = scale parameter
150 	+/
151 	this(ref UniformRNG rng, T shape, T scale)
152 	{
153 		this.rng = &rng;
154 		this.shape = shape;
155 		this.scale = scale;
156 	}
157 
158 	///
159 	T front() @property
160 	{
161 		return rng.rGamma(shape) * scale;
162 	}
163 	///
164 	static void popFront() @safe pure nothrow @nogc {}
165 	///
166 	enum empty = false;
167 
168 }
169 
170 ///
171 unittest
172 {
173 	import std.algorithm : map;
174 	import std.range;
175 	auto rng = GammaSRNG!double(rndGen, 1.1, 1.1);
176 	auto sample = rng.map!(x => x + 4).take(9).array;
177 }
178 
179 
180 /++
181 Class to generate random observations from a
182 inverse-gamma
183 distribution.
184  +/
185 struct InverseGammaSRNG(T, UniformRNG = Random)
186 	if (isFloatingPoint!T)
187 {
188 	private UniformRNG* rng;
189 	
190 	private T shape, scale;
191 
192 	/++
193 	Constructor
194 	Params:
195 		rng = uniform random number generator
196 		shape = shape parameter
197 		scale = scale parameter
198 	+/
199 	this(ref UniformRNG rng, T shape, T scale)
200 	{
201 		this.rng = &rng;
202 		this.shape = shape;
203 		this.scale = scale;
204 	}
205 
206 	///
207 	T front() @property
208 	{
209 		return rng.rInverseGamma(shape) * scale;
210 	}
211 	///
212 	static void popFront() @safe pure nothrow @nogc {}
213 	///
214 	enum empty = false;
215 
216 }
217 
218 ///
219 unittest
220 {
221 	import std.algorithm : map;
222 	import std.range;
223 	auto rng = InverseGammaSRNG!double(rndGen, 1.1, 1.1);
224 	auto sample = rng.map!(x => x + 4).take(9).array;
225 }
226 
227 
228 /++
229 Class to generate random observations from a
230 generalized gamma
231 distribution.
232  +/
233 struct GeneralizedGammaSRNG(T, UniformRNG = Random)
234 	if (isFloatingPoint!T)
235 {
236 	private UniformRNG* rng;
237 	
238 	private T shape, power, scale;
239 
240 	/++
241 	Constructor
242 	Params:
243 		rng = uniform random number generator
244 		shape = shape parameter
245 		power = power parameter
246 		scale = scale parameter
247 	+/
248 	this(ref UniformRNG rng, T shape, T power, T scale)
249 	{
250 		this.rng = &rng;
251 		this.shape = shape;
252 		this.power = power;
253 		this.scale = scale;
254 	}
255 
256 	///
257 	T front() @property
258 	{
259 		return rng.rGeneralizedGamma(shape, power) * scale;
260 	}
261 	///
262 	static void popFront() @safe pure nothrow @nogc {}
263 	///
264 	enum empty = false;
265 
266 }
267 
268 ///
269 unittest
270 {
271 	import std.algorithm : map;
272 	import std.range;
273 	auto rng = GeneralizedGammaSRNG!double(rndGen, 1.1, 1.1, 1.1);
274 	auto sample = rng.map!(x => x + 4).take(9).array;
275 }
276 
277 
278 /++
279 Class to generate random observations from a
280 inverse Gaussian
281 distribution.
282  +/
283 struct InverseGaussianSRNG(T, UniformRNG = Random)
284 	if (isFloatingPoint!T)
285 {
286 	private UniformRNG* rng;
287 	
288 	private T mu, lambda;
289 
290 	/++
291 	Constructor
292 	Params:
293 		rng = uniform random number generator
294 		lambda = parameter lambda
295 		mu = parameter mu
296 	+/
297 	this(ref UniformRNG rng, T lambda, T mu)
298 	{
299 		this.rng = &rng;
300 		this.lambda = lambda;
301 		this.mu = mu;
302 	}
303 
304 	///
305 	T front() @property
306 	{
307 		return rng.rInverseGaussian(lambda, mu);
308 	}
309 	///
310 	static void popFront() @safe pure nothrow @nogc {}
311 	///
312 	enum empty = false;
313 
314 }
315 
316 ///
317 unittest
318 {
319 	import std.algorithm : map;
320 	import std.range;
321 	auto rng = InverseGaussianSRNG!double(rndGen, 1.1, 1.1);
322 	auto sample = rng.map!(x => x + 4).take(9).array;
323 }
324 
325 
326 /++
327 Class to generate random observations from a
328 proper (chi > 0, psi > 0) generalized inverse Gaussian distribution. 
329 The algorithm is based on that given by Dagpunar (1989).
330 
331 References: $(LINK2 https://www.stat.auckland.ac.nz/~dscott/, Original R source code).
332 +/
333 struct ProperGeneralizedInverseGaussianSRNG(T, UniformRNG = Random)
334 	if (isFloatingPoint!T)
335 {
336 	private UniformRNG* rng;
337 	
338 	private T lambdam1, eta, omega, a, b, c, m;
339 
340 	/++
341 	Constructor
342 	Params:
343 		rng = uniform random number generator
344 		lambda = parameter lambda
345 		eta = sqrt(chi / psi)
346 		omega = sqrt(chi * psi)
347 	+/
348 	this(ref UniformRNG rng, T lambda, T eta, T omega)
349 	in {
350 		assert(lambda.isFinite);
351 		assert(eta.isNormal);
352 		assert(eta > 0);
353 		assert(omega.isNormal);
354 		assert(omega > 0);
355 	}
356 	body {
357 		import std.numeric : findRoot;
358 
359 		this.rng = &rng;
360 		this.lambdam1 = lambda - 1;
361 		this.omega= omega;
362 		this.eta = eta;
363 		this.m = (lambdam1 + hypot(lambdam1, omega)) / omega;
364 
365 		immutable a0 = 0.5f * omega;
366 		immutable a1 = -1 - lambda - m * a0;
367 		immutable a2 = lambdam1 * m - a0;
368 		immutable a3 = m * a0;
369 		//g = function(y) {
370 		//0.5f*omega*y^3 - y^2*(0.5f*omega*m + lambda + 1) +
371 		//y*(lambdam1*m - 0.5f*omega) + 0.5f*omega*m
372 		//}
373 		T g(T y) { return y * (y * (y * a0 + a1) + a2) + a3; }
374 		T upper = m;
375 		while (g(upper) <= 0)
376 			upper *= 2;
377 		immutable yM = findRoot(&g, 0, m);
378 		immutable yP = findRoot(&g, m, upper);
379 		immutable d = m + 1/m;
380 		immutable e = -0.25f * omega;
381 
382 		this.a = (yP - m) * pow(yP/m, 0.5f * lambdam1) * exp(e * (yP + 1/yP - d));
383 		this.b = (yM - m) * pow(yM/m, 0.5f * lambdam1) * exp(e * (yM + 1/yM - d));
384 		this.c = e * d + 0.5f * lambdam1 * log(m);
385 	}
386 
387 	///
388 	T front() @property
389 	{
390 		immutable c0 = -0.5f*lambdam1;
391 		immutable c1 = 0.25f * omega;
392 		for (;;)
393 		{
394 			immutable r1 = rng.uniform01!T;
395 			immutable r2 = rng.uniform01!T;
396 			immutable x = m + a * r2 / r1 + b * (1 - r2) / r1;
397 			if (x > 0 && -log(r1) >= c0 * log(x) + c1 * (x + 1/x) + c)
398 				return x * eta;
399 		}
400 	}
401 	///
402 	static void popFront() @safe pure nothrow @nogc {}
403 	///
404 	enum empty = false;
405 
406 }
407 
408 ///
409 unittest
410 {
411 	import std.algorithm : map;
412 	import std.range;
413 	auto rng = ProperGeneralizedInverseGaussianSRNG!double(rndGen, -2, 5.0, 2);
414 	auto sample = rng.map!(x => x + 4).take(9).array;
415 }
416 
417 
418 /++
419 Class to generate random observations from a
420 generalized inverse Gaussian 
421 distribution. 
422 
423 See_Also: [distribution.params](distribution/params.html)
424 +/
425 final class GeneralizedInverseGaussianRNG(T, UniformRNG = Random) : DistributionRNG!T
426 	if (isFloatingPoint!T)
427 {
428 	private DistributionRNG!T rng;
429 	
430 	/++
431 	Constructor
432 	Params:
433 		rng = uniform random number generator
434 		lambda = parameter lambda
435 		chi = chi parameter
436 		psi = psi parameter
437 	+/
438 	this(ref UniformRNG rng, T lambda, T chi, T psi)
439 	in {
440 		assert(lambda.isFinite);
441 		assert(chi.isFinite);
442 		assert(chi >= 0);
443 		assert(psi.isFinite);
444 		assert(psi >= 0);
445 	}
446 	body {
447 		immutable params = GIGChiPsi!T(chi, psi);
448 		if (chi < T.min_normal)
449 			this.rng = GammaSRNG!(T, UniformRNG)(rng, lambda, 2 / psi).toDistributionRNG;
450 		else if (psi < T.min_normal)
451 			this.rng = InverseGammaSRNG!(T, UniformRNG)(rng, -lambda, chi / 2).toDistributionRNG;
452 		else if (lambda == -0.5f)
453 			this.rng = InverseGaussianSRNG!(T, UniformRNG)(rng, params.eta, chi).toDistributionRNG;
454 		else
455 			this.rng = ProperGeneralizedInverseGaussianSRNG!(T, UniformRNG)(rng, lambda, params.eta, params.omega).toDistributionRNG;
456 	}
457 
458 	T front() @property
459 	{
460 		return rng.front;
461 	}
462 }
463 
464 ///
465 unittest
466 {
467 	import std.algorithm : map;
468 	import std.range;
469 	auto rng = new GeneralizedInverseGaussianRNG!double(rndGen, -2, 5.0, 2);
470 	auto sample = rng.map!(x => x + 4).take(9).array;
471 }
472 
473 
474 /++
475 Class to generate random observations from a
476 variance-gamma 
477 distribution using normal variance-mean mixture of 
478 gamma
479 distribution.
480 +/
481 final class VarianceGammaRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T
482 	if (isFloatingPoint!T)
483 {
484 	/++
485 	Constructor
486 	Params:
487 		rng = uniform random number generator
488 		shape = gamma shape parameter
489 		scale = gamma scale parameter
490 		beta = mixture scale parameter: `Y*U^(1/2)+beta*U`
491 	+/
492 	this(ref UniformRNG rng, T shape, T scale, T beta)
493 	{
494 		super(rng, GammaSRNG!(T, UniformRNG)(rng, shape, scale).toDistributionRNG, beta);
495 	}
496 }
497 
498 ///
499 unittest
500 {
501 	import std.algorithm : map;
502 	import std.range;
503 	auto rng = new VarianceGammaRNG!double(rndGen, 1.1, 1.1, 1.1);
504 	auto sample = rng.map!(x => x + 4).take(9).array;
505 }
506 
507 
508 /++
509 Class to generate random observations from a
510 hyperbolic asymmetric 
511 t-distribution using normal variance-mean mixture of 
512 inverse-gamma
513 distribution.
514 +/
515 final class HyperbolicAsymmetricTRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T
516 	if (isFloatingPoint!T)
517 {
518 	/++
519 	Constructor
520 	Params:
521 		rng = uniform random number generator
522 		shape = inverse-gamma shape parameter
523 		scale = inverse-gamma scale parameter
524 		beta = mixture scale parameter: `Y*U^(1/2)+beta*U`
525 	+/
526 	this(ref UniformRNG rng, T shape, T scale, T beta)
527 	{
528 		super(rng, InverseGammaSRNG!(T, UniformRNG)(rng, shape, scale).toDistributionRNG, beta);
529 	}
530 }
531 
532 ///
533 unittest
534 {
535 	import std.algorithm : map;
536 	import std.range;
537 	auto rng = new HyperbolicAsymmetricTRNG!double(rndGen, 1.1, 1.1, 1.1);
538 	auto sample = rng.map!(x => x + 4).take(9).array;
539 }
540 
541 
542 /++
543 Class to generate random observations from a
544 generalized variance-gamma 
545 distribution using normal variance-mean mixture of 
546 generalized gamma
547 distribution.
548 +/
549 final class GeneralizedVarianceGammaRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T
550 	if (isFloatingPoint!T)
551 {
552 	/++
553 	Constructor
554 	Params:
555 		rng = uniform random number generator
556 		shape = generalized gamma shape parameter
557 		power = generalized gamma power parameter
558 		scale = generalized gamma scale parameter
559 		beta = mixture scale parameter: `Y*U^(1/2)+beta*U`
560 	+/
561 	this(ref UniformRNG rng, T shape, T power, T scale, T beta)
562 	{
563 		super(rng, GeneralizedGammaSRNG!(T, UniformRNG)(rng, shape, power, scale).toDistributionRNG, beta);
564 	}
565 }
566 
567 ///
568 unittest
569 {
570 	import std.algorithm : map;
571 	import std.range;
572 	auto rng = new GeneralizedVarianceGammaRNG!double(rndGen, 1.1, 1.1, 1.1, 1.1);
573 	auto sample = rng.map!(x => x + 4).take(9).array;
574 }
575 
576 
577 /++
578 Class to generate random observations from a
579 normal inverse Gaussian 
580 distribution using normal variance-mean mixture of 
581 inverse Gaussian
582 distribution.
583 +/
584 final class NormalInverseGaussianRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T
585 	if (isFloatingPoint!T)
586 {
587 	/++
588 	Constructor
589 	Params:
590 		rng = uniform random number generator
591 		mu = inverse Gaussian parameter mu
592 		lambda = inverse Gaussian parameter lambda
593 		beta = mixture scale parameter: `Y*U^(1/2) + beta*U`
594 	+/
595 	this(ref UniformRNG rng, T lambda, T mu, T beta)
596 	{
597 		super(rng, InverseGaussianSRNG!(T, UniformRNG)(rng, mu, lambda).toDistributionRNG, beta);
598 	}
599 }
600 
601 ///
602 unittest
603 {
604 	import std.algorithm : map;
605 	import std.range;
606 	auto rng = new NormalInverseGaussianRNG!double(rndGen, 1.1, 1.1, 1.1);
607 	auto sample = rng.map!(x => x + 4).take(9).array;
608 }
609 
610 
611 /++
612 Class to generate random observations from a
613 proper generalized hyperbolic 
614 distribution using normal variance-mean mixture of 
615 proper generalized inverse Gaussian
616 distribution.
617 
618 See_Also: [distribution.params](distribution/params.html)
619 +/
620 final class ProperGeneralizedHyperbolicRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T
621 	if (isFloatingPoint!T)
622 {
623 	/++
624 	Constructor
625 	Params:
626 		rng = uniform random number generator
627 		lambda = proper generalized inverse Gaussian parameter lambda
628 		eta = proper generalized inverse Gaussian scale parameter
629 		omega = proper generalized inverse Gaussian concetration parameter
630 		beta = mixture scale parameter: `Y*U^(1/2)+beta*U`
631 	+/
632 	this(ref UniformRNG rng, T lambda, T eta, T omega, T beta)
633 	{
634 		super(rng, ProperGeneralizedInverseGaussianSRNG!(T, UniformRNG)(rng, lambda, eta, omega).toDistributionRNG, beta);
635 	}
636 }
637 
638 ///
639 unittest
640 {
641 	import std.algorithm : map;
642 	import std.range;
643 	auto rng = new ProperGeneralizedHyperbolicRNG!double(rndGen, 1.1, 1.1, 1.1, 1.1);
644 	auto sample = rng.map!(x => x + 4).take(9).array;
645 }
646 
647 
648 /++
649 Class to generate random observations from a
650 generalized hyperbolic 
651 distribution using normal variance-mean mixture of 
652 generalized inverse Gaussian
653 distribution.
654 
655 See_Also: [distribution.params](distribution/params.html)
656 +/
657 final class GeneralizedHyperbolicRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T
658 	if (isFloatingPoint!T)
659 {
660 	/++
661 	Constructor
662 	Params:
663 		rng = uniform random number generator
664 		lambda = generalized inverse Gaussian parameter lambda
665 		chi = generalized inverse Gaussian chi parameter
666 		psi = generalized inverse Gaussian psi parameter
667 		beta = mixture scale parameter: `Y*U^(1/2)+beta*U`
668 	+/
669 	this(ref UniformRNG rng, T lambda, T chi, T psi, T beta)
670 	{
671 		super(rng, new GeneralizedInverseGaussianRNG!(T, UniformRNG)(rng, lambda, chi, psi), beta);
672 	}
673 }
674 
675 ///
676 unittest
677 {
678 	import std.algorithm : map;
679 	import std.range;
680 	auto rng = new GeneralizedHyperbolicRNG!double(rndGen, 1.1, 1.1, 1.1, 1.1);
681 	auto sample = rng.map!(x => x + 4).take(9).array;
682 }
683 
684 
685 /++
686 Function to generate random observationb from
687 standard normal 
688 distribution.
689 +/
690 T rNormal(T = double)() 
691 	if (isFloatingPoint!T)
692 {
693 	return rndGen.rNormal!T;
694 }
695 
696 ///ditto
697 T rNormal(T = double, UniformRNG)(ref UniformRNG rng) 
698 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
699 {
700 	T p, p0, p1;
701 	do 
702 	{
703 		p0 = uniform!"[]"(-1.0, 1.0, rng);
704 		p1 = uniform!"[]"(-1.0, 1.0, rng);
705 		p = p0 * p0 + p1 * p1;
706 	} 
707 	while (p >= 1);
708 	return p0 * sqrt(-2 * log(p)/p);
709 }
710 
711 ///
712 unittest
713 {
714 	auto x = rNormal() * 5 + 1;
715 }
716 
717 
718 /++
719 Function to generate random observation from
720 standard exponential 
721 distribution.
722 +/
723 T rExponential(T = double)() 
724 	if (isFloatingPoint!T)
725 {
726 	return rndGen.rExponential!T;
727 }
728 
729 ///ditto
730 T rExponential(T = double, UniformRNG)(ref UniformRNG rng) 
731 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
732 {
733 	return -rng.uniform01!T.log;
734 }
735 
736 ///
737 unittest
738 {
739 	auto x = rExponential() * 5;
740 }
741 
742 
743 /++
744 Function to generate random observation from a
745 gamma 
746 distribution.
747 
748 References
749 	"Computer Generation of Statistical Distributions" by Richard Saucier
750 +/
751 T rGamma(T = double)(T shape) 
752 	if (isFloatingPoint!T)
753 {
754 	return rndGen.rGamma!T(shape);
755 }
756 
757 ///ditto
758 T rGamma(T = double, UniformRNG)(ref UniformRNG rng, T shape) 
759 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
760 in {
761 	assert(shape.isNormal);
762 	assert(shape > 0);
763 }
764 body {
765 	enum T E = 4.5;
766 	enum T D = 2.5040773967762740733732583523868748412194809812852436493487L; //1 + log( E );
767 
768 	if (shape == 1) 
769 		return rng.rExponential!T;
770 
771 	immutable A = 1 / sqrt( 2. * shape - 1 );
772 	immutable B = shape - log( 4. );
773 	immutable Q = shape + 1 / A;
774 	immutable C = 1 + shape / E;
775 	if (shape < 1) 
776 		for (;;)
777 		{
778 			T p = C * rng.uniform01!T;
779 			T y = void, f = void;
780 			if ( p > 1 )
781 			{
782 				y = -log( ( C - p ) / shape );
783 				f = pow(y, shape - 1);
784 			}
785 			else 
786 			{
787 				y = pow( p, 1 / shape );
788 				f = exp(-y);
789 			}
790 			if ( rng.uniform01!T <=  f) 
791 				return y;
792 		}
793 	else 
794 		for (;;)
795 		{
796 			T p0 = rng.uniform01!T;
797 			T p1 = rng.uniform01!T;
798 			T v = A * log( p0 / ( 1. - p0 ) );
799 			T y = shape * exp( v );
800 			T z = p0 * p0 * p1;
801 			T w = B + Q * v - y;
802 			if ( w + D - E * z >= 0.0 || w >= log(z) ) 
803 				return y;
804 		}
805 }
806 
807 ///
808 unittest
809 {
810 	auto x = rGamma(2.0) * 5;
811 }
812 
813 
814 /++
815 Function to generate random observation from a
816 generalized gamma 
817 distribution.
818 +/
819 T rGeneralizedGamma(T = double)(T shape, T power) 
820 	if (isFloatingPoint!T)
821 {
822 	return rndGen.rGeneralizedGamma!T(shape, power);
823 }
824 
825 ///ditto
826 T rGeneralizedGamma(T = double, UniformRNG)(ref UniformRNG rng, T shape, T power) 
827 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
828 in {
829 	assert(power.isFinite);
830 	assert(shape.isNormal);
831 	assert(shape > 0);
832 }
833 body {
834 	return rng.rGamma!T(shape).pow(1/power);
835 }
836 
837 ///
838 unittest
839 {
840 	auto x = rGeneralizedGamma(0.8, 3.0) * 5;
841 }
842 
843 
844 /++
845 Function to generate random observation from a
846 inverse-gamma 
847 distribution.
848 +/
849 T rInverseGamma(T = double)(T shape) 
850 	if (isFloatingPoint!T)
851 {
852 	return rndGen.rInverseGamma!T(shape);
853 }
854 
855 ///ditto
856 T rInverseGamma(T = double, UniformRNG)(ref UniformRNG rng, T shape) 
857 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
858 in {
859 	assert(shape.isNormal);
860 	assert(shape > 0);
861 }
862 body {
863 	return 1 / rng.rGamma!T(shape);
864 }
865 
866 ///
867 unittest
868 {
869 	auto x = rInverseGamma(2.0) * 5;
870 }
871 
872 
873 /++
874 Function to generate random observation from a
875 inverse Gaussian
876 distribution.
877 
878 References:
879 	Michael, John R.; Schucany, William R.; Haas, Roy W. (May 1976). "Generating Random Variates Using Transformations with Multiple Roots".
880  +/
881 T rInverseGaussian(T = double)(T lambda, T mu)
882 	if (isFloatingPoint!T)
883 {
884 	return rndGen.rInverseGaussian!T(lambda, mu);
885 }
886 
887 ///ditto
888 T rInverseGaussian(T = double, UniformRNG)(ref UniformRNG rng, T lambda, T mu)
889 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
890 in {
891 	assert(lambda.isNormal);
892 	assert(lambda > 0);
893 	assert(mu.isNormal);
894 	assert(mu > 0);
895 }
896 body {
897 	immutable nu = mu * mu;
898 	immutable v = rng.rNormal!T;
899 	lambda *= 2;
900 	immutable y = v * v * nu / lambda;
901 	immutable x = mu + (y - sqrt(y * (mu + y)));
902 	return rng.uniform01!T <= mu / (mu + x) ? x : nu / x;
903 }
904 
905 ///
906 unittest
907 {
908 	auto x = rInverseGaussian(2.0, 3.0) * 5;
909 }
910 
911 
912 /++
913 Function to generate random observation from a
914 Chi-squared
915 distribution.
916  +/
917 T rChiSquare(T = double)(T shape) 
918 	if (isFloatingPoint!T)
919 {
920 	return rndGen.rChiSquare!T(shape);
921 }
922 
923 ///ditto
924 T rChiSquare(T = double, UniformRNG)(ref UniformRNG rng, T shape) 
925 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
926 in {
927 	assert(shape.isNormal);
928 	assert(shape > 0);
929 }
930 body {
931 	return rng.rGamma(shape / 2) * 2;
932 }
933 
934 ///
935 unittest
936 {
937 	auto x = rChiSquare(2.0) * 5;
938 }
939 
940 
941 /++
942 Function to generate random observation from a
943 Student's t-distribution.
944  +/
945 T rStudentT(T = double)(T shape) 
946 	if (isFloatingPoint!T)
947 {
948 	return rndGen.rStudentT!T(shape);
949 }
950 
951 ///ditto
952 T rStudentT(T = double, UniformRNG)(ref UniformRNG rng, T shape) 
953 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
954 in {
955 	assert(shape.isNormal);
956 	assert(shape > 0);
957 }
958 body {
959 	return rng.rNormal!T / sqrt(rng.rChiSquare!T(shape)/shape);
960 }
961 
962 ///
963 unittest
964 {
965 	auto x = rStudentT(2.0) * 5;
966 }
967 
968 
969 /++
970 Function to generate random observation from a
971 Weibull
972 distribution.
973 +/
974 T rWeibull(T = double)(T shape) 
975 	if (isFloatingPoint!T)
976 {
977 	return rndGen.rWeibull!T(shape);
978 }
979 
980 ///ditto
981 T rWeibull(T = double, UniformRNG)(ref UniformRNG rng, T power) 
982 	if (isFloatingPoint!T && isUniformRNG!UniformRNG)
983 in {
984 	assert(power.isFinite);
985 }
986 body {
987 	return  rng.rExponential!T.pow(1/power);
988 }
989 
990 ///
991 unittest
992 {
993 	auto x = rWeibull(2.0) * 5 + 1;
994 }