Report a bug
If you spot a problem with this page, click here to create a Github issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

mir.random.variable

Authors:
Ilya Yaroshenko, Sebastian Wilzbach (DiscreteVariable)
enum RandomVariable;
User Defined Attribute definition for Random Variable.
template isRandomVariable(T)
Test if T is a random variable.
struct UniformVariable(T) if (isIntegral!T);

UniformVariable!T uniformVar(T)(in T a, in T b)
if (isIntegral!T);

UniformVariable!T uniformVariable(T = double)(in T a, in T b)
if (isIntegral!T);
Returns:
X ~ U[a, b]
Examples:
auto gen = Random(unpredictableSeed);
auto rv = uniformVar(-10, 10); // [-10, 10]
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(gen); // random variable
assert(rv.min == -10);
assert(rv.max == 10);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = UniformVariable!int(-10, 10); // [-10, 10]
auto x = rv(gen); // random variable
assert(rv.min == -10);
assert(rv.max == 10);
enum auto isRandomVariable;
this(T a, T b);

Constraints: a <= b.

T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
@property T min();
@property T max();
struct UniformVariable(T) if (isFloatingPoint!T);

UniformVariable!T uniformVar(T = double)(in T a, in T b)
if (isFloatingPoint!T);

UniformVariable!T uniformVariable(T = double)(in T a, in T b)
if (isFloatingPoint!T);
Returns:
X ~ U[a, b)
Examples:
import std.math : nextDown;
auto gen = Random(unpredictableSeed);
auto rv = uniformVar(-8.0, 10); // [-8, 10)
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(gen); // random variable
assert(rv.min == -8.0);
assert(rv.max == 10.0.nextDown);
Examples:
import std.math : nextDown;
auto gen = Random(unpredictableSeed);
auto rv = UniformVariable!double(-8, 10); // [-8, 10)
foreach(_; 0..1000)
{
    auto x = rv(gen);
    assert(rv.min <= x && x <= rv.max);
}
Examples:
import std.math : nextDown;
Random* gen = threadLocalPtr!Random;
auto rv = UniformVariable!double(-8, 10); // [-8, 10)
auto x = rv(gen); // random variable
assert(rv.min == -8.0);
assert(rv.max == 10.0.nextDown);
enum auto isRandomVariable;
this(T a, T b);

Constraints: a < b, a and b are finite numbers.

T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
@property T min();
@property T max();
struct ExponentialVariable(T) if (isFloatingPoint!T);

ExponentialVariable!T exponentialVar(T = double)(in T scale = 1)
if (isFloatingPoint!T);

alias exponentialVariable = exponentialVar(T = double)(in T scale = 1) if (isFloatingPoint!T);
Returns:
X ~ Exp(β)
Examples:
auto rv = exponentialVar;
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = ExponentialVariable!double(1);
auto x = rv(gen);
enum auto isRandomVariable;
this(T scale);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct WeibullVariable(T) if (isFloatingPoint!T);

WeibullVariable!T weibullVar(T = double)(T shape = 1, T scale = 1)
if (isFloatingPoint!T);

alias weibullVariable = weibullVar(T = double)(T shape = 1, T scale = 1) if (isFloatingPoint!T);
Examples:
auto gen = Random(unpredictableSeed);
auto rv = weibullVar;
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(gen);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = WeibullVariable!double(3, 2);
auto x = rv(gen);
enum auto isRandomVariable;
this(T shape, T scale);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct GammaVariable(T, bool Exp = false) if (isFloatingPoint!T);

GammaVariable!T gammaVar(T = double)(in T shape = 1, in T scale = 1)
if (isFloatingPoint!T);

alias gammaVariable = gammaVar(T = double)(in T shape = 1, in T scale = 1) if (isFloatingPoint!T);
Returns:
X ~ Gamma(𝝰, 𝞫)
Parameters:
T floating point type
Exp if true log-scaled values are produced. ExpGamma(𝝰, 𝞫). The flag is useful when shape parameter is small (𝝰 << 1).
Examples:
auto rv = gammaVar;
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = GammaVariable!double(1, 1);
auto x = rv(gen);
enum auto isRandomVariable;
this(T shape, T scale);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct BetaVariable(T) if (isFloatingPoint!T);

BetaVariable!T betaVar(T)(in T a, in T b)
if (isFloatingPoint!T);

alias betaVariable = betaVar(T)(in T a, in T b) if (isFloatingPoint!T);
Returns:
X ~ Beta(𝝰, 𝞫)
Examples:
auto rv = betaVariable(2.0, 5);
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
enum auto isRandomVariable;
this(T a, T b);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct ChiSquaredVariable(T) if (isFloatingPoint!T);

ChiSquaredVariable!T chiSquared(T = double)(size_t k)
if (isFloatingPoint!T);
Examples:
auto rv = chiSquared(3);
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = ChiSquaredVariable!double(3);
auto x = rv(gen);
enum auto isRandomVariable;
this(size_t k);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct FisherFVariable(T) if (isFloatingPoint!T);

FisherFVariable!T fisherFVar(T)(in T d1, in T d2)
if (isFloatingPoint!T);

alias fisherFVariable = fisherFVar(T)(in T d1, in T d2) if (isFloatingPoint!T);
Examples:
auto rv = fisherFVar(3.0, 4);
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
enum auto isRandomVariable;
this(T d1, T d2);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct StudentTVariable(T) if (isFloatingPoint!T);

StudentTVariable!T studentTVar(T)(in T nu)
if (isFloatingPoint!T);

alias studentTVariable = studentTVar(T)(in T nu) if (isFloatingPoint!T);
Examples:
auto rv = studentTVar(10.0);
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = StudentTVariable!double(10);
auto x = rv(gen);
enum auto isRandomVariable;
this(T nu);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct NormalVariable(T) if (isFloatingPoint!T);

NormalVariable!T normalVar(T = double)(in T location = 0.00000, in T scale = 1)
if (isFloatingPoint!T);

alias normalVariable = normalVar(T = double)(in T location = 0.00000, in T scale = 1) if (isFloatingPoint!T);
Returns:
X ~ N(μ, σ)
Examples:
auto rv = normalVar;
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = NormalVariable!double(0, 1);
auto x = rv(gen);
enum auto isRandomVariable;
this(T location, T scale);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct LogNormalVariable(T) if (isFloatingPoint!T);

LogNormalVariable!T logNormalVar(T = double)(in T normalLocation = 0.00000, in T normalScale = 1)
if (isFloatingPoint!T);

alias logNormalVariable = logNormalVar(T = double)(in T normalLocation = 0.00000, in T normalScale = 1) if (isFloatingPoint!T);
Examples:
auto rv = logNormalVar;
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = LogNormalVariable!double(0, 1);
auto x = rv(gen);
enum auto isRandomVariable;
this(T normalLocation, T normalScale);
Parameters:
T normalLocation location of associated normal
T normalScale scale of associated normal
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct CauchyVariable(T) if (isFloatingPoint!T);

CauchyVariable!T cauchyVar(T = double)(in T location = 0.00000, in T scale = 1)
if (isFloatingPoint!T);

alias cauchyVariable = cauchyVar(T = double)(in T location = 0.00000, in T scale = 1) if (isFloatingPoint!T);
Returns:
X ~ Cauchy(x, γ)
Examples:
auto rv = cauchyVar;
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = CauchyVariable!double(0, 1);
auto x = rv(gen);
enum auto isRandomVariable;
this(T location, T scale);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct ExtremeValueVariable(T) if (isFloatingPoint!T);

ExtremeValueVariable!T extremeValueVar(T = double)(in T location = 0.00000, in T scale = 1)
if (isFloatingPoint!T);

alias extremeValueVariable = extremeValueVar(T = double)(in T location = 0.00000, in T scale = 1) if (isFloatingPoint!T);
Examples:
auto rv = extremeValueVar;
static assert(isRandomVariable!(typeof(rv)));
auto x = rv(rne);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = ExtremeValueVariable!double(0, 1);
auto x = rv(gen);
enum auto isRandomVariable;
this(T location, T scale);
T opCall(G)(ref scope G gen)
if (isSaturatedRandomEngine!G);

T opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum T min;
enum T max;
struct BernoulliVariable(T) if (isFloatingPoint!T);

BernoulliVariable!T bernoulliVar(T)(in T p)
if (isFloatingPoint!T);

alias bernoulliVariable = bernoulliVar(T)(in T p) if (isFloatingPoint!T);
Examples:
auto rv = bernoulliVar(0.7);
static assert(isRandomVariable!(typeof(rv)));
int[2] hist;
foreach(_; 0..1000)
    hist[rv(rne)]++;
//import std.stdio;
//writeln(hist);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = BernoulliVariable!double(0.7);
int[2] hist;
foreach(_; 0..10)
    hist[rv(gen)]++;
enum auto isRandomVariable;
this(T p);
Parameters:
T p true probability
bool opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);

bool opCall(RNG)(scope RNG* gen)
if (isSaturatedRandomEngine!RNG);
enum bool min;
enum bool max;
struct Bernoulli2Variable;

Bernoulli2Variable bernoulli2Var()();

alias bernoulli2Variable = bernoulli2Var()();
Bernoulli random variable. A fast specialization for p := 1/2.
Examples:
auto rv = bernoulli2Var;
static assert(isRandomVariable!(typeof(rv)));
int[2] hist;
foreach(_; 0..1000)
    hist[rv(rne)]++;
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = Bernoulli2Variable.init;
int[2] hist;
foreach(_; 0..10)
    hist[rv(gen)]++;
enum bool isRandomVariable;
bool opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);

bool opCall(RNG)(scope RNG* gen)
if (isSaturatedRandomEngine!RNG);
enum bool min;
enum bool max;
struct GeometricVariable(T) if (isFloatingPoint!T);

GeometricVariable!T geometricVar(T)(in T p, bool success = true)
if (isFloatingPoint!T);

alias geometricVariable = geometricVar(T)(in T p, bool success = true) if (isFloatingPoint!T);
Examples:
auto rv = geometricVar(0.1);
static assert(isRandomVariable!(typeof(rv)));
size_t[ulong] hist;
foreach(_; 0..1000)
    hist[rv(rne)]++;
//import std.stdio;
//foreach(i; 0..100)
//    if(auto count = i in hist)
//        write(*count, ", ");
//    else
//        write("0, ");
//writeln();
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = GeometricVariable!double(0.1, true);
size_t[ulong] hist;
foreach(_; 0..10)
    hist[rv(gen)]++;
enum auto isRandomVariable;
this(T p, bool success);
Parameters:
T p probability
bool success p is success probability if true and failure probability otherwise.
ulong opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);

ulong opCall(RNG)(scope RNG* gen)
if (isSaturatedRandomEngine!RNG);
enum ulong min;
enum ulong max;
struct PoissonVariable(T) if (isFloatingPoint!T);

PoissonVariable!T poissonVar(T = double)(in T rate = 1.00000)
if (isFloatingPoint!T);

alias poissonVariable = poissonVar(T = double)(in T rate = 1.00000) if (isFloatingPoint!T);
Examples:
import mir.random;
auto rv = poissonVar;
static assert(isRandomVariable!(typeof(rv)));
size_t[ulong] hist;
foreach(_; 0..1000)
    hist[rv(rne)]++;
//import std.stdio;
//foreach(i; 0..100)
//    if(auto count = i in hist)
//        write(*count, ", ");
//    else
//        write("0, ");
//writeln();
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = PoissonVariable!double(10);
size_t[ulong] hist;
foreach(_; 0..10)
    hist[rv(gen)]++;
enum auto isRandomVariable;
this(T rate);
Parameters:
T rate rate
ulong opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);

ulong opCall(G)(scope G* gen)
if (isSaturatedRandomEngine!G);
enum ulong min;
enum ulong max;
struct NegativeBinomialVariable(T) if (isFloatingPoint!T);

NegativeBinomialVariable!T negativeBinomialVar(T)(size_t r, in T p)
if (isFloatingPoint!T);

alias negativeBinomialVariable = negativeBinomialVar(T)(size_t r, in T p) if (isFloatingPoint!T);
Examples:
import mir.random;
auto rv = negativeBinomialVar(30, 0.3);
static assert(isRandomVariable!(typeof(rv)));
size_t[ulong] hist;
foreach(_; 0..1000)
    hist[rv(rne)]++;
//import std.stdio;
//foreach(i; 0..100)
//    if(auto count = i in hist)
//        write(*count, ", ");
//    else
//        write("0, ");
//writeln();
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = NegativeBinomialVariable!double(30, 0.3);
size_t[ulong] hist;
foreach(_; 0..10)
    hist[rv(gen)]++;
enum auto isRandomVariable;
this(size_t r, T p);
Parameters:
size_t r r > 0; number of failures until the experiment is stopped
T p p ∈ (0,1); success probability in each experiment
ulong opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);

ulong opCall(RNG)(scope RNG* gen)
if (isSaturatedRandomEngine!RNG);
enum ulong min;
enum ulong max;
struct BinomialVariable(T) if (isFloatingPoint!T);

BinomialVariable!T binomialVar(T)(size_t r, in T p)
if (isFloatingPoint!T);

alias binomialVariable = binomialVar(T)(size_t r, in T p) if (isFloatingPoint!T);
Examples:
import mir.random;
auto rv = binomialVar(20, 0.5);
static assert(isRandomVariable!(typeof(rv)));
int[] hist = new int[rv.max + 1];
auto cnt = 1000;
foreach(_; 0..cnt)
    hist[rv(rne)]++;
//import std.stdio;
//foreach(n, e; hist)
//    writefln("p(x = %s) = %s", n, double(e) / cnt);
Examples:
Random* gen = threadLocalPtr!Random;
auto rv = BinomialVariable!double(20, 0.5);
int[] hist = new int[rv.max + 1];
auto cnt = 10;
foreach(_; 0..cnt)
    hist[rv(gen)]++;
enum auto isRandomVariable;
this(size_t n, T p);
Parameters:
size_t n n > 0; number of trials
T p p ∈ [0,1]; success probability in each trial
size_t opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);
size_t opCall(RNG)(scope RNG* gen)
if (isSaturatedRandomEngine!RNG);
enum size_t min;
@property size_t max();
struct DiscreteVariable(T) if (isNumeric!T);

DiscreteVariable!T discreteVar(T)(T[] weights, bool cumulative = false)
if (isNumeric!T);

alias discreteVariable = discreteVar(T)(T[] weights, bool cumulative = false) if (isNumeric!T);
Discrete distribution sampler that draws random values from a discrete distribution given an array of the respective probability density points (weights).
Examples:
auto gen = Random(unpredictableSeed);
// 10%, 20%, 20%, 40%, 10%
auto weights = [10.0, 20, 20, 40, 10];
auto ds = discreteVar(weights);
static assert(isRandomVariable!(typeof(ds)));

// weight is changed to cumulative sums
assert(weights == [10, 30, 50, 90, 100]);

// sample from the discrete distribution
auto obs = new uint[weights.length];

foreach (i; 0..1000)
    obs[ds(gen)]++;

//import std.stdio;
//writeln(obs);
Examples:
Cumulative
auto gen = Random(unpredictableSeed);

auto cumulative = [10.0, 30, 40, 90, 120];
auto ds = discreteVar(cumulative, true);

assert(cumulative == [10.0, 30, 40, 90, 120]);

// sample from the discrete distribution
auto obs = new uint[cumulative.length];
foreach (i; 0..1000)
    obs[ds(gen)]++;
Examples:
auto gen = Random(unpredictableSeed);
// 10%, 20%, 20%, 40%, 10%
auto weights = [10.0, 20, 20, 40, 10];
auto ds = discreteVar(weights);

// weight is changed to cumulative sums
assert(weights == [10, 30, 50, 90, 100]);

// sample from the discrete distribution
auto obs = new uint[weights.length];
foreach (i; 0..1000)
    obs[ds(gen)]++;

//import std.stdio;
//writeln(obs);
//[999, 1956, 2063, 3960, 1022]
enum auto isRandomVariable;
this(T[] weights, bool cumulative);
DiscreteVariable constructor computes cumulative density points in place without memory allocation.
Parameters:
T[] weights density points
bool cumulative optional flag indiciates if weights are already cumulative
size_t opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);

size_t opCall(RNG)(scope RNG* gen)
if (isSaturatedRandomEngine!RNG);
Samples a value from the discrete distribution using a custom random generator.

Complexity: O(log n) where n is the number of weights.

enum size_t min;
@property size_t max();
struct PiecewiseConstantVariable(T, W = T) if (isNumeric!T && isNumeric!W);

PiecewiseConstantVariable!(T, W) piecewiseConstantVar(T, W)(T[] intervals, W[] weights, bool cumulative = false)
if (isNumeric!T && isNumeric!W);

alias piecewiseConstantVariable = piecewiseConstantVar(T, W)(T[] intervals, W[] weights, bool cumulative = false) if (isNumeric!T && isNumeric!W);
Piecewise constant variable.
Examples:
// 50% of the time, generate a random number between 0 and 1
// 50% of the time, generate a random number between 10 and 15
double[] i = [0,  1, 10, 15];
double[] w =   [1,  0,  1];
auto pcv = piecewiseConstantVar(i, w);
static assert(isRandomVariable!(typeof(pcv)));
assert(w == [1, 1, 2]);

int[int] hist;
foreach(_; 0 .. 10000)
    ++hist[cast(int)pcv(rne)];

//import std.stdio;
//import mir.ndslice.topology: repeat;
//foreach(j; 0..cast(int)i[$-1])
//    if(auto count = j in hist)
//        writefln("%2s %s", j, '*'.repeat(*count / 100));

//////// output example /////////
/+
 0 **************************************************
10 *********
11 *********
12 **********
13 *********
14 **********
+/
Examples:
Random* gen = threadLocalPtr!Random;
// 50% of the time, generate a random number between 0 and 1
// 50% of the time, generate a random number between 10 and 15
double[] i = [0,  1, 10, 15];
double[] w =   [1,  0,  1];
auto pcv = piecewiseConstantVar(i, w);
assert(w == [1, 1, 2]);

int[int] hist;
foreach(_; 0 .. 10)
    ++hist[cast(int)pcv(gen)];
enum auto isRandomVariable;
this(T[] intervals, W[] weights, bool cumulative);
PiecewiseConstantVariable constructor computes cumulative density points in place without memory allocation.
Parameters:
T[] intervals strictly increasing sequence of interval bounds.
W[] weights density points
bool cumulative optional flag indicates if weights are already cumulative
T opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);

T opCall(RNG)(scope RNG* gen)
if (isSaturatedRandomEngine!RNG);

Complexity: O(log n) where n is the number of weights.

@property T min();
@property T max();
struct PiecewiseLinearVariable(T) if (isFloatingPoint!T);

PiecewiseLinearVariable!T piecewiseLinearVar(T)(T[] points, T[] weights, T[] areas)
if (isFloatingPoint!T);

alias piecewiseLinearVariable = piecewiseLinearVar(T)(T[] points, T[] weights, T[] areas) if (isFloatingPoint!T);
Piecewise constant variable.
Examples:
auto gen = Random(unpredictableSeed);
// increase the probability from 0 to 5
// remain flat from 5 to 10
// decrease from 10 to 15 at the same rate
double[] i = [0, 5, 10, 15];
double[] w = [0, 1,  1,  0];
auto pcv = piecewiseLinearVar(i, w, new double[w.length - 1]);
static assert(isRandomVariable!(typeof(pcv)));

int[int] hist;
foreach(_; 0 .. 10000)
    ++hist[cast(int)pcv(gen)];

//import std.stdio;
//import mir.ndslice.topology: repeat;
//foreach(j; 0..cast(int)i[$-1]+1)
//    if(auto count = j in hist)
//        writefln("%2s %s", j, '*'.repeat(*count / 100));

//////// output example /////////
/+
 0 *
 1 **
 2 *****
 3 *******
 4 ********
 5 **********
 6 *********
 7 *********
 8 **********
 9 *********
10 *********
11 *******
12 ****
13 **
14 *
+/
Examples:
Random* gen = threadLocalPtr!Random;
// increase the probability from 0 to 5
// remain flat from 5 to 10
// decrease from 10 to 15 at the same rate
double[] i = [0, 5, 10, 15];
double[] w = [0, 1,  1,  0];
auto pcv = PiecewiseLinearVariable!double(i, w, new double[w.length - 1]);

int[int] hist;
foreach(_; 0 .. 10)
    ++hist[cast(int)pcv(gen)];
enum auto isRandomVariable;
this(T[] points, T[] weights, T[] areas);
Parameters:
T[] points strictly increasing sequence of interval bounds.
T[] weights density points
T[] areas user allocated uninitialized array

Constrains: points.length == weights.length
areas.length > 0
areas.length + 1 == weights.length

T opCall(RNG)(ref scope RNG gen)
if (isSaturatedRandomEngine!RNG);

T opCall(RNG)(scope RNG* gen)
if (isSaturatedRandomEngine!RNG);

Complexity: O(log n) where n is the number of weights.

@property T min();
@property T max();