reference

This documentation is automatically generated from the openFrameworks source code using doxygen and refers to the most recent release, version 0.12.0.

ofRandomDistributions.h
Go to the documentation of this file.
1#ifndef OF_RANDOM_DISTRIBUTIONS_H_
2#define OF_RANDOM_DISTRIBUTIONS_H_
3
4#include <random>
5#include <glm/glm.hpp>
6
7// https://gist.github.com/imneme/540829265469e673d045
8// https://github.com/effolkronium/random/tree/master
9// https://www.theanalysisfactor.com/differences-between-normal-and-poisson-distributions
10// https://blogs.sas.com/content/iml/2019/07/22/extreme-value-normal-data.html
11// https://rovdownloads.com/blog/quick-overview-of-probability-distributions/
12
13namespace of::random
14{
15
16// MARK: - UNIFORM
17
18// special case of distinguishing real and integer for uniform distribution
19
20template<typename T = float, typename ... Args>
21std::enable_if_t<std::is_floating_point_v<T>, T>
22uniform(Args&&... args) {
23 std::uniform_real_distribution<T> distr(std::forward<Args>(args)...);
24 return distr(of::random::gen());
25}
26
27template<typename T, typename ... Args>
28std::enable_if_t<std::is_integral_v<T>, T>
29uniform(Args&&... args) {
30 std::uniform_int_distribution<T> distr(std::forward<Args>(args)...);
31 return distr(of::random::gen());
32}
33
34template <typename T>
35std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
36uniform(double min, double max) {
37 std::uniform_real_distribution<float> dist(min,max);
38 return {
39 dist(of::random::gen()),
40 dist(of::random::gen())
41 };
42}
43
44template <typename T>
45std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
46uniform(T min, T max) {
47 return {
48 std::uniform_real_distribution<float>{min.x, max.x}(of::random::gen()),
49 std::uniform_real_distribution<float>{min.y, max.y}(of::random::gen())
50 };
51}
52
53template <typename T, typename ... Args>
54std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
55uniform(Args&&... args) {
56 std::uniform_real_distribution<float> dist(std::forward<Args>(args)...);
57 return {
58 dist(of::random::gen()),
59 dist(of::random::gen()),
60 dist(of::random::gen())
61 };
62}
63
64template <typename T>
65std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
66uniform(T min, T max) {
67 return {
68 std::uniform_real_distribution<float>{min.x, max.x}(of::random::gen()),
69 std::uniform_real_distribution<float>{min.y, max.y}(of::random::gen()),
70 std::uniform_real_distribution<float>{min.z, max.z}(of::random::gen())
71 };
72}
73
74// MARK: NORMAL (gaussian)
75
76// better to cast params as double (otherwise casual ints produce unexpected results)
77
78template<typename T = float>
79std::enable_if_t<std::is_arithmetic_v<T>, T>
80normal(T mean, T stddev) {
81 return std::normal_distribution<T>{mean, stddev}(of::random::gen());
82}
83
84template <typename T>
85std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
86normal(float mean, float stddev) {
87 std::normal_distribution<float> dist(mean, stddev);
88 return {
89 dist(of::random::gen()),
90 dist(of::random::gen())
91 };
92}
93
94template <typename T>
95std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
96normal(T mean, T stddev) {
97 return {
98 std::normal_distribution<float>{mean.x, stddev.x}(of::random::gen()),
99 std::normal_distribution<float>{mean.y, stddev.y}(of::random::gen())
100 };
101}
102
103template <typename T>
104std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
105normal(float mean, float stddev) {
106 std::normal_distribution<float> dist(mean, stddev);
107 return {
108 dist(of::random::gen()),
109 dist(of::random::gen()),
110 dist(of::random::gen())
111 };
112}
113
114template <typename T>
115std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
116normal(T mean, T stddev) {
117 return {
118 std::normal_distribution<float>{mean.x, stddev.x}(of::random::gen()),
119 std::normal_distribution<float>{mean.y, stddev.y}(of::random::gen()),
120 std::normal_distribution<float>{mean.z, stddev.z}(of::random::gen())
121 };
122}
123
124// MARK: LOGNORMAL
125
126template<typename T = float>
127std::enable_if_t<std::is_arithmetic_v<T>, T>
128lognormal(double mean, double stddev) {
129 return std::lognormal_distribution{mean, stddev}(of::random::gen());
130}
131
132template <typename T>
133std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
134lognormal(double mean, double stddev) {
135 std::lognormal_distribution dist(mean, stddev);
136 return {
137 dist(of::random::gen()),
138 dist(of::random::gen())
139 };
140}
141
142template <typename T>
143std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
144lognormal(T mean, T stddev) {
145 return {
146 std::lognormal_distribution{mean.x, stddev.x}(of::random::gen()),
147 std::lognormal_distribution{mean.y, stddev.y}(of::random::gen())
148 };
149}
150
151template <typename T>
152std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
153lognormal(double mean, double stddev) {
154 std::lognormal_distribution dist(mean, stddev);
155 return {
156 dist(of::random::gen()),
157 dist(of::random::gen()),
158 dist(of::random::gen())
159 };
160}
161
162template <typename T>
163std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
164lognormal(T mean, T stddev) {
165 return {
166 std::lognormal_distribution{mean.x, stddev.x}(of::random::gen()),
167 std::lognormal_distribution{mean.y, stddev.y}(of::random::gen()),
168 std::lognormal_distribution{mean.z, stddev.z}(of::random::gen())
169 };
170}
171
172// MARK: GAMMA
173
174template<typename T = float>
175std::enable_if_t<std::is_arithmetic_v<T>, T>
176gamma(double alpha, double beta) {
177 return std::gamma_distribution{alpha, beta}(of::random::gen());
178}
179
180template <typename T>
181std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
182gamma(double alpha, double beta) {
183 std::gamma_distribution dist(alpha, beta);
184 return {
185 dist(of::random::gen()),
186 dist(of::random::gen())
187 };
188}
189
190template <typename T>
191std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
192gamma(T alpha, T beta) {
193 return {
194 std::gamma_distribution{alpha.x, beta.x}(of::random::gen()),
195 std::gamma_distribution{alpha.y, beta.y}(of::random::gen())
196 };
197}
198
199template <typename T>
200std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
201gamma(double alpha, double beta) {
202 std::gamma_distribution dist(alpha, beta);
203 return {
204 dist(of::random::gen()),
205 dist(of::random::gen()),
206 dist(of::random::gen())
207 };
208}
209
210template <typename T>
211std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
212gamma(T alpha, T beta) {
213 return {
214 std::gamma_distribution{alpha.x, beta.x}(of::random::gen()),
215 std::gamma_distribution{alpha.y, beta.y}(of::random::gen()),
216 std::gamma_distribution{alpha.z, beta.z}(of::random::gen())
217 };
218}
219// MARK: POISSON
220
221template<typename T = float>
222std::enable_if_t<std::is_arithmetic_v<T>, T>
223poisson(double mean) {
224 return std::poisson_distribution<T>{mean}(of::random::gen());
225}
226
227template <typename T>
228std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
229poisson(double mean) {
230 std::poisson_distribution<T> dist(mean);
231 return {
232 dist(of::random::gen()),
233 dist(of::random::gen())
234 };
235}
236
237template <typename T>
238std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
239poisson(T mean) {
240 return {
241 std::poisson_distribution<T>{mean.x}(of::random::gen()),
242 std::poisson_distribution<T>{mean.y}(of::random::gen())
243 };
244}
245
246template <typename T>
247std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
248poisson(double mean) {
249 std::poisson_distribution<T> dist(mean);
250 return {
251 dist(of::random::gen()),
252 dist(of::random::gen()),
253 dist(of::random::gen())
254 };
255}
256
257template <typename T>
258std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
259poisson(T mean) {
260 return {
261 std::poisson_distribution<T>{mean.x}(of::random::gen()),
262 std::poisson_distribution<T>{mean.y}(of::random::gen()),
263 std::poisson_distribution<T>{mean.z}(of::random::gen())
264 };
265}
266
267// MARK: EXPONENTIAL
268
269template<typename T = float>
270std::enable_if_t<std::is_arithmetic_v<T>, T>
271exponential(T lambda) {
272 return std::exponential_distribution<T>{lambda}(of::random::gen());
273}
274
275template <typename T>
276std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
277exponential(double lambda) {
278 std::exponential_distribution<T> dist(lambda);
279 return {
280 dist(of::random::gen()),
281 dist(of::random::gen())
282 };
283}
284
285template <typename T>
286std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
287exponential(T lambda) {
288 return {
289 std::exponential_distribution<T>{lambda.x}(of::random::gen()),
290 std::exponential_distribution<T>{lambda.y}(of::random::gen())
291 };
292}
293
294template <typename T>
295std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
296exponential(double lambda) {
297 std::exponential_distribution<T> dist(lambda);
298 return {
299 dist(of::random::gen()),
300 dist(of::random::gen()),
301 dist(of::random::gen())
302 };
303}
304
305template <typename T>
306std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
307exponential(T lambda) {
308 return {
309 std::exponential_distribution<T>{lambda.x}(of::random::gen()),
310 std::exponential_distribution<T>{lambda.y}(of::random::gen()),
311 std::exponential_distribution<T>{lambda.z}(of::random::gen())
312 };
313}
314
315// MARK: CHI_SQUARED
316
317template<typename T = float>
318std::enable_if_t<std::is_arithmetic_v<T>, T>
320 return std::chi_squared_distribution<T>{n}(of::random::gen());
321}
322
323template <typename T>
324std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
325chi_squared(double n) {
326 std::chi_squared_distribution<T> dist(n);
327 return {
328 dist(of::random::gen()),
329 dist(of::random::gen())
330 };
331}
332
333template <typename T>
334std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
336 return {
337 std::chi_squared_distribution<T>{n.x}(of::random::gen()),
338 std::chi_squared_distribution<T>{n.y}(of::random::gen())
339 };
340}
341
342template <typename T>
343std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
344chi_squared(double n) {
345 std::chi_squared_distribution<T> dist(n);
346 return {
347 dist(of::random::gen()),
348 dist(of::random::gen()),
349 dist(of::random::gen())
350 };
351}
352
353template <typename T>
354std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
356 return {
357 std::chi_squared_distribution<T>{n.x}(of::random::gen()),
358 std::chi_squared_distribution<T>{n.y}(of::random::gen()),
359 std::chi_squared_distribution<T>{n.z}(of::random::gen())
360 };
361}
362
363// MARK: BINOMIAL
364
365template<typename T = int>
366std::enable_if_t<std::is_arithmetic_v<T>, T>
367binomial(int p, double t) {
368 if (t>=1) {
369 std::cout << "of::random::binomial(): t must be < 1.0\n";
370 return 0;
371 } else {
372 std::binomial_distribution<T> dist(p,t);
373 return dist(of::random::gen());
374 }
375}
376
377template <typename T>
378std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
379binomial(int p, double t) {
380 std::binomial_distribution<int> dist(p,t);
381 return {
382 dist(of::random::gen()),
383 dist(of::random::gen())
384 };
385}
386
387template <typename T>
388std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
389binomial(T p,T t) {
390 return {
391 std::binomial_distribution<int>{p.x,t.x}(of::random::gen()),
392 std::binomial_distribution<int>{p.y,t.y}(of::random::gen())
393 };
394}
395
396template <typename T>
397std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
398binomial(int p, double t) {
399 std::binomial_distribution<int> dist(p,t);
400 return {
401 dist(of::random::gen()),
402 dist(of::random::gen()),
403 dist(of::random::gen())
404 };
405}
406
407template <typename T>
408std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
409binomial(T p,T t) {
410 return {
411 std::binomial_distribution{p.x,t.x}(of::random::gen()),
412 std::binomial_distribution{p.y,t.y}(of::random::gen()),
413 std::binomial_distribution{p.z,t.z}(of::random::gen())
414 };
415}
416
417// MARK: GEOMETRIC
418
419template<typename T = int>
420std::enable_if_t<std::is_arithmetic_v<T>, T>
422 return std::geometric_distribution{}(of::random::gen());
423}
424
425template <typename T>
426std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
428 std::geometric_distribution<int> dist;
429 return {
430 dist(of::random::gen()),
431 dist(of::random::gen())
432 };
433}
434
435template <typename T>
436std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
438 std::geometric_distribution<int> dist;
439 return {
440 dist(of::random::gen()),
441 dist(of::random::gen()),
442 dist(of::random::gen())
443 };
444}
445
446// MARK: BERNOUILLI
447
448template <typename T = bool>
449auto bernoulli(double p) {
450 return std::bernoulli_distribution(p)(of::random::gen());
451}
452
453template <typename T>
454std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
455bernoulli(double p) {
456 std::bernoulli_distribution dist(p);
457 return {
458 dist(of::random::gen()),
459 dist(of::random::gen())
460 };
461}
462
463template <typename T>
464std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
466 return {
467 std::bernoulli_distribution{p.x}(of::random::gen()),
468 std::bernoulli_distribution{p.y}(of::random::gen())
469 };
470}
471
472template <typename T>
473std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
474bernoulli(double p) {
475 std::bernoulli_distribution dist(p);
476 return {
477 dist(of::random::gen()),
478 dist(of::random::gen()),
479 dist(of::random::gen())
480 };
481}
482
483template <typename T>
484std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
486 return {
487 std::bernoulli_distribution{p.x}(of::random::gen()),
488 std::bernoulli_distribution{p.y}(of::random::gen()),
489 std::bernoulli_distribution{p.z}(of::random::gen())
490 };
491}
492// MARK: - CONVENIENT FORWARDING ALIASES
493
494template <typename... Args>
495auto gaussian(Args&&... args) -> decltype(normal(std::forward<Args>(args)...)) {
496 return normal(std::forward<Args>(args)...);
497}
498
499template <typename... Args>
500auto yes(Args&&... args) -> decltype(bernoulli(std::forward<Args>(args)...)) {
501 return bernoulli(std::forward<Args>(args)...);
502}
503
504// whilst nerdy enough, the above do not work with {initializer list} for glm::vec parameters such as:
505// of::random::gaussian<glm::vec2>({0,10},{1,100});
506// so this additional explicit nerdiness is required to wrap them:
507
508template<class T>
509auto gaussian(T min, T max) -> decltype(normal<T>(min, max)) {
510 return normal(min, max);
511}
512template<class T>
513auto yes(T p) -> decltype(bernoulli(p)) {
514 return bernoulli(p);
515}
516
517// MARK: - OTHER FUNCTIONS
518
519template<class T = float> // works for all non-refined
520T bound_normal(float min, float max, float focus = 4.0f) {
521 if (min >= max) {
522 std::cout << "ofRandomNormalLimits()" << "max must be > than min\n";
523 return {};
524 } else {
525 if (focus <= .0099) {
526 std::cout << "ofRandomNormalLimits()" << "focus must be at least .01\n";
527 return {};
528 } else {
529 T v;
530 do { v = of::random::normal<T>((max+min)/2.0f, (max-min)/(2*focus)); } while (v < min || v > max);
531 return v;
532 }
533 }
534}
535
536template <typename T>
537std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
538bound_normal(T min, T max, T focus = {4.0f, 4.0f}) {
539 if (min.x >= max.x || min.y >= max.y) {
540 std::cout << "ofRandomNormalLimits()" << "max must be > than min\n";
541 return {};
542 } else {
543 if (focus.x < 1 || focus.y < 1 ) {
544 std::cout << "ofRandomNormalLimits()" << "focus must be at least 1\n";
545 return {};
546 } else {
547 T v;
548 do { v.x = of::random::normal<float>((max.x+min.x)/2.0f, (max.x-min.x)/(2*focus.x)); } while (v.x < min.x || v.x > max.x);
549 do { v.y = of::random::normal<float>((max.y+min.y)/2.0f, (max.y-min.y)/(2*focus.y)); } while (v.y < min.y || v.y > max.y);
550 return v;
551 }
552 }
553}
554
555template <typename T>
556std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
557bound_normal(T min, T max, T focus = {4.0f, 4.0f, 4.0f}) {
558 if (min.x >= max.x || min.y >= max.y || min.z >= max.z) {
559 std::cout << "ofRandomNormalLimits()" << "max must be > than min\n";
560 return {};
561 } else {
562 if (focus.x < 1 || focus.y < 1 || focus.z < 1) {
563 std::cout << "ofRandomNormalLimits()" << "focus must be at least 1\n";
564 return {};
565 } else {
566 T v;
567 do { v.x = of::random::normal<float>((max.x+min.x)/2.0f, (max.x-min.x)/(2*focus.x)); } while (v.x < min.x || v.x > max.x);
568 do { v.y = of::random::normal<float>((max.y+min.y)/2.0f, (max.y-min.y)/(2*focus.y)); } while (v.y < min.y || v.y > max.y);
569 do { v.z = of::random::normal<float>((max.z+min.z)/2.0f, (max.z-min.z)/(2*focus.z)); } while (v.z < min.z || v.z > max.z);
570 return v;
571 }
572 }
573}
574
575} // end namespace of::random
576
577// MARK: - ANONYMOUS NAMESPACE FUNCTIONS
578
579namespace {
580
581// again, 2 templates per function, for the convenience of implicit brace init of glm::vec parameters
582
583template<class T = float, typename ... Args>
584T ofRandomUniform(Args&&... args) { return of::random::uniform<T>(std::forward<Args>(args)...); }
585
586template<class T = float>
587T ofRandomUniform(T min, T max) { return of::random::uniform<T>(min, max); }
588
589template<class T, typename ... Args>
590T ofRandomNormal(Args&&... args) { return of::random::normal<T>(std::forward<Args>(args)...); }
591
592template<class T>
593T ofRandomNormal(T min, T max) { return of::random::normal<T>(min, max); }
594
595template<class T, typename ... Args>
596T ofRandomGaussian(Args&&... args) { return of::random::gaussian<T>(std::forward<Args>(args)...); }
597
598template<class T>
599T ofRandomGaussian(T mean, T stddev) { return of::random::gaussian<T>(mean, stddev); }
600
601template<class T, typename ... Args>
602T ofRandomBernoulli(Args&&... args) { return of::random::bernoulli<T>(std::forward<Args>(args)...); }
603
604template<class T>
605T ofRandomBernoulli(T prob) { return of::random::bernoulli<T>(prob); }
606
607template<class T, typename ... Args>
608T ofRandomYes(Args&&... args) { return of::random::yes<T>(std::forward<Args>(args)...); }
609
610template<class T>
611T ofRandomYes(T prob) { return of::random::yes<T>(prob); }
612
613template<class T, typename ... Args>
614T ofRandomPoisson(Args&&... args) { return of::random::poisson<T>(std::forward<Args>(args)...); }
615
616template<class T>
617T ofRandomPoisson(T mean) { return of::random::poisson<T>(mean); }
618
619template<class T, typename ... Args>
620T ofRandomExponential(Args&&... args) { return of::random::exponential<T>(std::forward<Args>(args)...); }
621
622template<class T>
623T ofRandomExponential(T lambda) { return of::random::exponential<T>(lambda); }
624
625template<class T, typename ... Args>
626T ofRandomChiSquared(Args&&... args) { return of::random::chi_squared<T>(std::forward<Args>(args)...); }
627
628template<class T>
629T ofRandomChiSquared(T freedom) { return of::random::chi_squared<T>(freedom); }
630
631template<class T, typename ... Args>
632T ofRandomGamma(Args&&... args) { return of::random::gamma<T>(std::forward<Args>(args)...); }
633
634template<class T>
635T ofRandomGamma(T a, T b) { return of::random::gamma<T>(a, b); }
636
637template<class T, typename ... Args>
638T ofRandomLogNormal(Args&&... args) { return of::random::lognormal<T>(std::forward<Args>(args)...); }
639
640template<class T>
641T ofRandomLogNormal(T mean, T stddev) { return of::random::lognormal<T>(mean, stddev); }
642
643template<class T, typename ... Args>
644T ofRandomGeometric(Args&&... args) { return of::random::geometric<T>(std::forward<Args>(args)...); }
645
646template<class T>
647T ofRandomGeometric() { return of::random::geometric<T>(); }
648
649template<class T = float>
650T ofRandomBoundNormal(float min, float max, float focus = 4.0f) {
651 return of::random::bound_normal(min, max, focus);
652}
653
654template <typename T>
655std::enable_if_t<std::is_same_v<T, glm::vec2>, T>
656ofRandomBoundNormal(T min, T max, T focus = {4.0f, 4.0f}) {
657 return of::random::bound_normal(min, max, focus);
658}
659
660template <typename T>
661std::enable_if_t<std::is_same_v<T, glm::vec3>, T>
662ofRandomBoundNormal(T min, T max, T focus = {4.0f, 4.0f, 4.0f}) {
663 return of::random::bound_normal(min, max, focus);
664}
665
666} // end anonymous namespace
667#endif // OF_RANDOM_DISTRIBUTIONS_H_
Definition ofRandomDistributions.h:14
std::enable_if_t< std::is_arithmetic_v< T >, T > gamma(double alpha, double beta)
Definition ofRandomDistributions.h:176
std::enable_if_t< std::is_arithmetic_v< T >, T > geometric()
Definition ofRandomDistributions.h:421
std::enable_if_t< std::is_arithmetic_v< T >, T > lognormal(double mean, double stddev)
Definition ofRandomDistributions.h:128
std::enable_if_t< std::is_arithmetic_v< T >, T > normal(T mean, T stddev)
Definition ofRandomDistributions.h:80
auto & gen()
Definition ofRandomEngine.h:57
std::enable_if_t< std::is_arithmetic_v< T >, T > poisson(double mean)
Definition ofRandomDistributions.h:223
auto yes(Args &&... args) -> decltype(bernoulli(std::forward< Args >(args)...))
Definition ofRandomDistributions.h:500
auto bernoulli(double p)
Definition ofRandomDistributions.h:449
std::enable_if_t< std::is_floating_point_v< T >, T > uniform(Args &&... args)
Definition ofRandomDistributions.h:22
std::enable_if_t< std::is_arithmetic_v< T >, T > binomial(int p, double t)
Definition ofRandomDistributions.h:367
T bound_normal(float min, float max, float focus=4.0f)
Definition ofRandomDistributions.h:520
std::enable_if_t< std::is_arithmetic_v< T >, T > chi_squared(T n)
Definition ofRandomDistributions.h:319
auto gaussian(Args &&... args) -> decltype(normal(std::forward< Args >(args)...))
Definition ofRandomDistributions.h:495
std::enable_if_t< std::is_arithmetic_v< T >, T > exponential(T lambda)
Definition ofRandomDistributions.h:271
#define a
#define b