aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/random.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/random.d')
-rw-r--r--libphobos/src/std/random.d3344
1 files changed, 3344 insertions, 0 deletions
diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d
new file mode 100644
index 0000000..b3116e18
--- /dev/null
+++ b/libphobos/src/std/random.d
@@ -0,0 +1,3344 @@
+// Written in the D programming language.
+
+/**
+Facilities for random number generation.
+
+$(RED Disclaimer:) The _random number generators and API provided in this
+module are not designed to be cryptographically secure, and are therefore
+unsuitable for cryptographic or security-related purposes such as generating
+authentication tokens or network sequence numbers. For such needs, please use a
+reputable cryptographic library instead.
+
+The new-style generator objects hold their own state so they are
+immune of threading issues. The generators feature a number of
+well-known and well-documented methods of generating random
+numbers. An overall fast and reliable means to generate random numbers
+is the $(D_PARAM Mt19937) generator, which derives its name from
+"$(LINK2 https://en.wikipedia.org/wiki/Mersenne_Twister, Mersenne Twister)
+with a period of 2 to the power of
+19937". In memory-constrained situations,
+$(LINK2 https://en.wikipedia.org/wiki/Linear_congruential_generator,
+linear congruential generators) such as $(D MinstdRand0) and $(D MinstdRand) might be
+useful. The standard library provides an alias $(D_PARAM Random) for
+whichever generator it considers the most fit for the target
+environment.
+
+In addition to random number generators, this module features
+distributions, which skew a generator's output statistical
+distribution in various ways. So far the uniform distribution for
+integers and real numbers have been implemented.
+
+Source: $(PHOBOSSRC std/_random.d)
+
+Macros:
+
+Copyright: Copyright Andrei Alexandrescu 2008 - 2009, Joseph Rushton Wakeling 2012.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP erdani.org, Andrei Alexandrescu)
+ Masahiro Nakagawa (Xorshift random generator)
+ $(HTTP braingam.es, Joseph Rushton Wakeling) (Algorithm D for random sampling)
+ Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-_random, mir-_random))
+Credits: The entire random number library architecture is derived from the
+ excellent $(HTTP open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf, C++0X)
+ random number facility proposed by Jens Maurer and contributed to by
+ researchers at the Fermi laboratory (excluding Xorshift).
+*/
+/*
+ Copyright Andrei Alexandrescu 2008 - 2009.
+Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+*/
+module std.random;
+
+
+import std.range.primitives;
+import std.traits;
+
+///
+@safe unittest
+{
+ // seed a random generator with a constant
+ auto rnd = Random(42);
+
+ // Generate a uniformly-distributed integer in the range [0, 14]
+ // If no random generator is passed, the global `rndGen` would be used
+ auto i = uniform(0, 15, rnd);
+ assert(i == 12);
+
+ // Generate a uniformly-distributed real in the range [0, 100)
+ auto r = uniform(0.0L, 100.0L, rnd);
+ assert(r == 79.65429843861011285);
+
+ // Generate a 32-bit random number
+ auto u = uniform!uint(rnd);
+ assert(u == 4083286876);
+}
+
+version (unittest)
+{
+ static import std.meta;
+ package alias PseudoRngTypes = std.meta.AliasSeq!(MinstdRand0, MinstdRand, Mt19937, Xorshift32, Xorshift64,
+ Xorshift96, Xorshift128, Xorshift160, Xorshift192);
+}
+
+// Segments of the code in this file Copyright (c) 1997 by Rick Booth
+// From "Inner Loops" by Rick Booth, Addison-Wesley
+
+// Work derived from:
+
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+/**
+ * Test if Rng is a random-number generator. The overload
+ * taking a ElementType also makes sure that the Rng generates
+ * values of that type.
+ *
+ * A random-number generator has at least the following features:
+ * $(UL
+ * $(LI it's an InputRange)
+ * $(LI it has a 'bool isUniformRandom' field readable in CTFE)
+ * )
+ */
+template isUniformRNG(Rng, ElementType)
+{
+ enum bool isUniformRNG = isInputRange!Rng &&
+ is(typeof(Rng.front) == ElementType) &&
+ is(typeof(
+ {
+ static assert(Rng.isUniformRandom); //tag
+ }));
+}
+
+/**
+ * ditto
+ */
+template isUniformRNG(Rng)
+{
+ enum bool isUniformRNG = isInputRange!Rng &&
+ is(typeof(
+ {
+ static assert(Rng.isUniformRandom); //tag
+ }));
+}
+
+/**
+ * Test if Rng is seedable. The overload
+ * taking a SeedType also makes sure that the Rng can be seeded with SeedType.
+ *
+ * A seedable random-number generator has the following additional features:
+ * $(UL
+ * $(LI it has a 'seed(ElementType)' function)
+ * )
+ */
+template isSeedable(Rng, SeedType)
+{
+ enum bool isSeedable = isUniformRNG!(Rng) &&
+ is(typeof(
+ {
+ Rng r = void; // can define a Rng object
+ r.seed(SeedType.init); // can seed a Rng
+ }));
+}
+
+///ditto
+template isSeedable(Rng)
+{
+ enum bool isSeedable = isUniformRNG!Rng &&
+ is(typeof(
+ {
+ Rng r = void; // can define a Rng object
+ r.seed(typeof(r.front).init); // can seed a Rng
+ }));
+}
+
+@safe pure nothrow unittest
+{
+ struct NoRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+ }
+ static assert(!isUniformRNG!(NoRng, uint));
+ static assert(!isUniformRNG!(NoRng));
+ static assert(!isSeedable!(NoRng, uint));
+ static assert(!isSeedable!(NoRng));
+
+ struct NoRng2
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+
+ enum isUniformRandom = false;
+ }
+ static assert(!isUniformRNG!(NoRng2, uint));
+ static assert(!isUniformRNG!(NoRng2));
+ static assert(!isSeedable!(NoRng2, uint));
+ static assert(!isSeedable!(NoRng2));
+
+ struct NoRng3
+ {
+ @property bool empty() {return false;}
+ void popFront() {}
+
+ enum isUniformRandom = true;
+ }
+ static assert(!isUniformRNG!(NoRng3, uint));
+ static assert(!isUniformRNG!(NoRng3));
+ static assert(!isSeedable!(NoRng3, uint));
+ static assert(!isSeedable!(NoRng3));
+
+ struct validRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+
+ enum isUniformRandom = true;
+ }
+ static assert(isUniformRNG!(validRng, uint));
+ static assert(isUniformRNG!(validRng));
+ static assert(!isSeedable!(validRng, uint));
+ static assert(!isSeedable!(validRng));
+
+ struct seedRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+ void seed(uint val){}
+ enum isUniformRandom = true;
+ }
+ static assert(isUniformRNG!(seedRng, uint));
+ static assert(isUniformRNG!(seedRng));
+ static assert(isSeedable!(seedRng, uint));
+ static assert(isSeedable!(seedRng));
+}
+
+/**
+Linear Congruential generator.
+ */
+struct LinearCongruentialEngine(UIntType, UIntType a, UIntType c, UIntType m)
+if (isUnsigned!UIntType)
+{
+ ///Mark this as a Rng
+ enum bool isUniformRandom = true;
+ /// Does this generator have a fixed range? ($(D_PARAM true)).
+ enum bool hasFixedRange = true;
+ /// Lowest generated value ($(D 1) if $(D c == 0), $(D 0) otherwise).
+ enum UIntType min = ( c == 0 ? 1 : 0 );
+ /// Highest generated value ($(D modulus - 1)).
+ enum UIntType max = m - 1;
+/**
+The parameters of this distribution. The random number is $(D_PARAM x
+= (x * multipler + increment) % modulus).
+ */
+ enum UIntType multiplier = a;
+ ///ditto
+ enum UIntType increment = c;
+ ///ditto
+ enum UIntType modulus = m;
+
+ static assert(isIntegral!(UIntType));
+ static assert(m == 0 || a < m);
+ static assert(m == 0 || c < m);
+ static assert(m == 0 ||
+ (cast(ulong) a * (m-1) + c) % m == (c < a ? c - a + m : c - a));
+
+ // Check for maximum range
+ private static ulong gcd(ulong a, ulong b) @safe pure nothrow @nogc
+ {
+ while (b)
+ {
+ auto t = b;
+ b = a % b;
+ a = t;
+ }
+ return a;
+ }
+
+ private static ulong primeFactorsOnly(ulong n) @safe pure nothrow @nogc
+ {
+ ulong result = 1;
+ ulong iter = 2;
+ for (; n >= iter * iter; iter += 2 - (iter == 2))
+ {
+ if (n % iter) continue;
+ result *= iter;
+ do
+ {
+ n /= iter;
+ } while (n % iter == 0);
+ }
+ return result * n;
+ }
+
+ @safe pure nothrow unittest
+ {
+ static assert(primeFactorsOnly(100) == 10);
+ //writeln(primeFactorsOnly(11));
+ static assert(primeFactorsOnly(11) == 11);
+ static assert(primeFactorsOnly(7 * 7 * 7 * 11 * 15 * 11) == 7 * 11 * 15);
+ static assert(primeFactorsOnly(129 * 2) == 129 * 2);
+ // enum x = primeFactorsOnly(7 * 7 * 7 * 11 * 15);
+ // static assert(x == 7 * 11 * 15);
+ }
+
+ private static bool properLinearCongruentialParameters(ulong m,
+ ulong a, ulong c) @safe pure nothrow @nogc
+ {
+ if (m == 0)
+ {
+ static if (is(UIntType == uint))
+ {
+ // Assume m is uint.max + 1
+ m = (1uL << 32);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ // Bounds checking
+ if (a == 0 || a >= m || c >= m) return false;
+ // c and m are relatively prime
+ if (c > 0 && gcd(c, m) != 1) return false;
+ // a - 1 is divisible by all prime factors of m
+ if ((a - 1) % primeFactorsOnly(m)) return false;
+ // if a - 1 is multiple of 4, then m is a multiple of 4 too.
+ if ((a - 1) % 4 == 0 && m % 4) return false;
+ // Passed all tests
+ return true;
+ }
+
+ // check here
+ static assert(c == 0 || properLinearCongruentialParameters(m, a, c),
+ "Incorrect instantiation of LinearCongruentialEngine");
+
+/**
+Constructs a $(D_PARAM LinearCongruentialEngine) generator seeded with
+$(D x0).
+ */
+ this(UIntType x0) @safe pure
+ {
+ seed(x0);
+ }
+
+/**
+ (Re)seeds the generator.
+*/
+ void seed(UIntType x0 = 1) @safe pure
+ {
+ static if (c == 0)
+ {
+ import std.exception : enforce;
+ enforce(x0, "Invalid (zero) seed for "
+ ~ LinearCongruentialEngine.stringof);
+ }
+ _x = modulus ? (x0 % modulus) : x0;
+ popFront();
+ }
+
+/**
+ Advances the random sequence.
+*/
+ void popFront() @safe pure nothrow @nogc
+ {
+ static if (m)
+ {
+ static if (is(UIntType == uint) && m == uint.max)
+ {
+ immutable ulong
+ x = (cast(ulong) a * _x + c),
+ v = x >> 32,
+ w = x & uint.max;
+ immutable y = cast(uint)(v + w);
+ _x = (y < v || y == uint.max) ? (y + 1) : y;
+ }
+ else static if (is(UIntType == uint) && m == int.max)
+ {
+ immutable ulong
+ x = (cast(ulong) a * _x + c),
+ v = x >> 31,
+ w = x & int.max;
+ immutable uint y = cast(uint)(v + w);
+ _x = (y >= int.max) ? (y - int.max) : y;
+ }
+ else
+ {
+ _x = cast(UIntType) ((cast(ulong) a * _x + c) % m);
+ }
+ }
+ else
+ {
+ _x = a * _x + c;
+ }
+ }
+
+/**
+ Returns the current number in the random sequence.
+*/
+ @property UIntType front() const @safe pure nothrow @nogc
+ {
+ return _x;
+ }
+
+///
+ @property typeof(this) save() @safe pure nothrow @nogc
+ {
+ return this;
+ }
+
+/**
+Always $(D false) (random generators are infinite ranges).
+ */
+ enum bool empty = false;
+
+/**
+ Compares against $(D_PARAM rhs) for equality.
+ */
+ bool opEquals(ref const LinearCongruentialEngine rhs) const @safe pure nothrow @nogc
+ {
+ return _x == rhs._x;
+ }
+
+ private UIntType _x = m ? (a + c) % m : (a + c);
+}
+
+/**
+Define $(D_PARAM LinearCongruentialEngine) generators with well-chosen
+parameters. $(D MinstdRand0) implements Park and Miller's "minimal
+standard" $(HTTP
+wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator,
+generator) that uses 16807 for the multiplier. $(D MinstdRand)
+implements a variant that has slightly better spectral behavior by
+using the multiplier 48271. Both generators are rather simplistic.
+ */
+alias MinstdRand0 = LinearCongruentialEngine!(uint, 16_807, 0, 2_147_483_647);
+/// ditto
+alias MinstdRand = LinearCongruentialEngine!(uint, 48_271, 0, 2_147_483_647);
+
+///
+@safe unittest
+{
+ // seed with a constant
+ auto rnd0 = MinstdRand0(1);
+ auto n = rnd0.front; // same for each run
+ // Seed with an unpredictable value
+ rnd0.seed(unpredictableSeed);
+ n = rnd0.front; // different across runs
+}
+
+@safe unittest
+{
+ import std.range;
+ static assert(isForwardRange!MinstdRand);
+ static assert(isUniformRNG!MinstdRand);
+ static assert(isUniformRNG!MinstdRand0);
+ static assert(isUniformRNG!(MinstdRand, uint));
+ static assert(isUniformRNG!(MinstdRand0, uint));
+ static assert(isSeedable!MinstdRand);
+ static assert(isSeedable!MinstdRand0);
+ static assert(isSeedable!(MinstdRand, uint));
+ static assert(isSeedable!(MinstdRand0, uint));
+
+ // The correct numbers are taken from The Database of Integer Sequences
+ // http://www.research.att.com/~njas/sequences/eisBTfry00128.txt
+ auto checking0 = [
+ 16807UL,282475249,1622650073,984943658,1144108930,470211272,
+ 101027544,1457850878,1458777923,2007237709,823564440,1115438165,
+ 1784484492,74243042,114807987,1137522503,1441282327,16531729,
+ 823378840,143542612 ];
+ //auto rnd0 = MinstdRand0(1);
+ MinstdRand0 rnd0;
+
+ foreach (e; checking0)
+ {
+ assert(rnd0.front == e);
+ rnd0.popFront();
+ }
+ // Test the 10000th invocation
+ // Correct value taken from:
+ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf
+ rnd0.seed();
+ popFrontN(rnd0, 9999);
+ assert(rnd0.front == 1043618065);
+
+ // Test MinstdRand
+ auto checking = [48271UL,182605794,1291394886,1914720637,2078669041,
+ 407355683];
+ //auto rnd = MinstdRand(1);
+ MinstdRand rnd;
+ foreach (e; checking)
+ {
+ assert(rnd.front == e);
+ rnd.popFront();
+ }
+
+ // Test the 10000th invocation
+ // Correct value taken from:
+ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf
+ rnd.seed();
+ popFrontN(rnd, 9999);
+ assert(rnd.front == 399268537);
+
+ // Check .save works
+ foreach (Type; std.meta.AliasSeq!(MinstdRand0, MinstdRand))
+ {
+ auto rnd1 = Type(unpredictableSeed);
+ auto rnd2 = rnd1.save;
+ assert(rnd1 == rnd2);
+ // Enable next test when RNGs are reference types
+ version (none) { assert(rnd1 !is rnd2); }
+ assert(rnd1.take(100).array() == rnd2.take(100).array());
+ }
+}
+
+/**
+The $(LINK2 https://en.wikipedia.org/wiki/Mersenne_Twister, Mersenne Twister) generator.
+ */
+struct MersenneTwisterEngine(UIntType, size_t w, size_t n, size_t m, size_t r,
+ UIntType a, size_t u, UIntType d, size_t s,
+ UIntType b, size_t t,
+ UIntType c, size_t l, UIntType f)
+if (isUnsigned!UIntType)
+{
+ static assert(0 < w && w <= UIntType.sizeof * 8);
+ static assert(1 <= m && m <= n);
+ static assert(0 <= r && 0 <= u && 0 <= s && 0 <= t && 0 <= l);
+ static assert(r <= w && u <= w && s <= w && t <= w && l <= w);
+ static assert(0 <= a && 0 <= b && 0 <= c);
+ static assert(n <= sizediff_t.max);
+
+ ///Mark this as a Rng
+ enum bool isUniformRandom = true;
+
+/**
+Parameters for the generator.
+*/
+ enum size_t wordSize = w;
+ enum size_t stateSize = n; /// ditto
+ enum size_t shiftSize = m; /// ditto
+ enum size_t maskBits = r; /// ditto
+ enum UIntType xorMask = a; /// ditto
+ enum size_t temperingU = u; /// ditto
+ enum UIntType temperingD = d; /// ditto
+ enum size_t temperingS = s; /// ditto
+ enum UIntType temperingB = b; /// ditto
+ enum size_t temperingT = t; /// ditto
+ enum UIntType temperingC = c; /// ditto
+ enum size_t temperingL = l; /// ditto
+ enum UIntType initializationMultiplier = f; /// ditto
+
+ /// Smallest generated value (0).
+ enum UIntType min = 0;
+ /// Largest generated value.
+ enum UIntType max = UIntType.max >> (UIntType.sizeof * 8u - w);
+ // note, `max` also serves as a bitmask for the lowest `w` bits
+ static assert(a <= max && b <= max && c <= max && f <= max);
+
+ /// The default seed value.
+ enum UIntType defaultSeed = 5489u;
+
+ // Bitmasks used in the 'twist' part of the algorithm
+ private enum UIntType lowerMask = (cast(UIntType) 1u << r) - 1,
+ upperMask = (~lowerMask) & this.max;
+
+ /*
+ Collection of all state variables
+ used by the generator
+ */
+ private struct State
+ {
+ /*
+ State array of the generator. This
+ is iterated through backwards (from
+ last element to first), providing a
+ few extra compiler optimizations by
+ comparison to the forward iteration
+ used in most implementations.
+ */
+ UIntType[n] data;
+
+ /*
+ Cached copy of most recently updated
+ element of `data` state array, ready
+ to be tempered to generate next
+ `front` value
+ */
+ UIntType z;
+
+ /*
+ Most recently generated random variate
+ */
+ UIntType front;
+
+ /*
+ Index of the entry in the `data`
+ state array that will be twisted
+ in the next `popFront()` call
+ */
+ size_t index;
+ }
+
+ /*
+ State variables used by the generator;
+ initialized to values equivalent to
+ explicitly seeding the generator with
+ `defaultSeed`
+ */
+ private State state = defaultState();
+ /* NOTE: the above is a workaround to ensure
+ backwards compatibility with the original
+ implementation, which permitted implicit
+ construction. With `@disable this();`
+ it would not be necessary. */
+
+/**
+ Constructs a MersenneTwisterEngine object.
+*/
+ this(UIntType value) @safe pure nothrow @nogc
+ {
+ seed(value);
+ }
+
+ /**
+ Generates the default initial state for a Mersenne
+ Twister; equivalent to the internal state obtained
+ by calling `seed(defaultSeed)`
+ */
+ private static State defaultState() @safe pure nothrow @nogc
+ {
+ if (!__ctfe) assert(false);
+ State mtState;
+ seedImpl(defaultSeed, mtState);
+ return mtState;
+ }
+
+/**
+ Seeds a MersenneTwisterEngine object.
+ Note:
+ This seed function gives 2^w starting points (the lowest w bits of
+ the value provided will be used). To allow the RNG to be started
+ in any one of its internal states use the seed overload taking an
+ InputRange.
+*/
+ void seed()(UIntType value = defaultSeed) @safe pure nothrow @nogc
+ {
+ this.seedImpl(value, this.state);
+ }
+
+ /**
+ Implementation of the seeding mechanism, which
+ can be used with an arbitrary `State` instance
+ */
+ private static void seedImpl(UIntType value, ref State mtState)
+ {
+ mtState.data[$ - 1] = value;
+ static if (this.max != UIntType.max)
+ {
+ mtState.data[$ - 1] &= this.max;
+ }
+
+ foreach_reverse (size_t i, ref e; mtState.data[0 .. $ - 1])
+ {
+ e = f * (mtState.data[i + 1] ^ (mtState.data[i + 1] >> (w - 2))) + cast(UIntType)(n - (i + 1));
+ static if (this.max != UIntType.max)
+ {
+ e &= this.max;
+ }
+ }
+
+ mtState.index = n - 1;
+
+ /* double popFront() to guarantee both `mtState.z`
+ and `mtState.front` are derived from the newly
+ set values in `mtState.data` */
+ MersenneTwisterEngine.popFrontImpl(mtState);
+ MersenneTwisterEngine.popFrontImpl(mtState);
+ }
+
+/**
+ Seeds a MersenneTwisterEngine object using an InputRange.
+
+ Throws:
+ $(D Exception) if the InputRange didn't provide enough elements to seed the generator.
+ The number of elements required is the 'n' template parameter of the MersenneTwisterEngine struct.
+ */
+ void seed(T)(T range) if (isInputRange!T && is(Unqual!(ElementType!T) == UIntType))
+ {
+ this.seedImpl(range, this.state);
+ }
+
+ /**
+ Implementation of the range-based seeding mechanism,
+ which can be used with an arbitrary `State` instance
+ */
+ private static void seedImpl(T)(T range, ref State mtState)
+ if (isInputRange!T && is(Unqual!(ElementType!T) == UIntType))
+ {
+ size_t j;
+ for (j = 0; j < n && !range.empty; ++j, range.popFront())
+ {
+ sizediff_t idx = n - j - 1;
+ mtState.data[idx] = range.front;
+ }
+
+ mtState.index = n - 1;
+
+ if (range.empty && j < n)
+ {
+ import core.internal.string : UnsignedStringBuf, unsignedToTempString;
+
+ UnsignedStringBuf buf = void;
+ string s = "MersenneTwisterEngine.seed: Input range didn't provide enough elements: Need ";
+ s ~= unsignedToTempString(n, buf, 10) ~ " elements.";
+ throw new Exception(s);
+ }
+
+ /* double popFront() to guarantee both `mtState.z`
+ and `mtState.front` are derived from the newly
+ set values in `mtState.data` */
+ MersenneTwisterEngine.popFrontImpl(mtState);
+ MersenneTwisterEngine.popFrontImpl(mtState);
+ }
+
+/**
+ Advances the generator.
+*/
+ void popFront() @safe pure nothrow @nogc
+ {
+ this.popFrontImpl(this.state);
+ }
+
+ /*
+ Internal implementation of `popFront()`, which
+ can be used with an arbitrary `State` instance
+ */
+ private static void popFrontImpl(ref State mtState)
+ {
+ /* This function blends two nominally independent
+ processes: (i) calculation of the next random
+ variate `mtState.front` from the cached previous
+ `data` entry `z`, and (ii) updating the value
+ of `data[index]` and `mtState.z` and advancing
+ the `index` value to the next in sequence.
+
+ By interweaving the steps involved in these
+ procedures, rather than performing each of
+ them separately in sequence, the variables
+ are kept 'hot' in CPU registers, allowing
+ for significantly faster performance. */
+ sizediff_t index = mtState.index;
+ sizediff_t next = index - 1;
+ if (next < 0)
+ next = n - 1;
+ auto z = mtState.z;
+ sizediff_t conj = index - m;
+ if (conj < 0)
+ conj = index - m + n;
+
+ static if (d == UIntType.max)
+ {
+ z ^= (z >> u);
+ }
+ else
+ {
+ z ^= (z >> u) & d;
+ }
+
+ auto q = mtState.data[index] & upperMask;
+ auto p = mtState.data[next] & lowerMask;
+ z ^= (z << s) & b;
+ auto y = q | p;
+ auto x = y >> 1;
+ z ^= (z << t) & c;
+ if (y & 1)
+ x ^= a;
+ auto e = mtState.data[conj] ^ x;
+ z ^= (z >> l);
+ mtState.z = mtState.data[index] = e;
+ mtState.index = next;
+
+ /* technically we should take the lowest `w`
+ bits here, but if the tempering bitmasks
+ `b` and `c` are set correctly, this should
+ be unnecessary */
+ mtState.front = z;
+ }
+
+/**
+ Returns the current random value.
+ */
+ @property UIntType front() @safe const pure nothrow @nogc
+ {
+ return this.state.front;
+ }
+
+///
+ @property typeof(this) save() @safe pure nothrow @nogc
+ {
+ return this;
+ }
+
+/**
+Always $(D false).
+ */
+ enum bool empty = false;
+}
+
+/**
+A $(D MersenneTwisterEngine) instantiated with the parameters of the
+original engine $(HTTP math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html,
+MT19937), generating uniformly-distributed 32-bit numbers with a
+period of 2 to the power of 19937. Recommended for random number
+generation unless memory is severely restricted, in which case a $(D
+LinearCongruentialEngine) would be the generator of choice.
+ */
+alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31,
+ 0x9908b0df, 11, 0xffffffff, 7,
+ 0x9d2c5680, 15,
+ 0xefc60000, 18, 1_812_433_253);
+
+///
+@safe unittest
+{
+ // seed with a constant
+ Mt19937 gen;
+ auto n = gen.front; // same for each run
+ // Seed with an unpredictable value
+ gen.seed(unpredictableSeed);
+ n = gen.front; // different across runs
+}
+
+@safe nothrow unittest
+{
+ import std.algorithm;
+ import std.range;
+ static assert(isUniformRNG!Mt19937);
+ static assert(isUniformRNG!(Mt19937, uint));
+ static assert(isSeedable!Mt19937);
+ static assert(isSeedable!(Mt19937, uint));
+ static assert(isSeedable!(Mt19937, typeof(map!((a) => unpredictableSeed)(repeat(0)))));
+ Mt19937 gen;
+ assert(gen.front == 3499211612);
+ popFrontN(gen, 9999);
+ assert(gen.front == 4123659995);
+ try { gen.seed(iota(624u)); } catch (Exception) { assert(false); }
+ assert(gen.front == 3708921088u);
+ popFrontN(gen, 9999);
+ assert(gen.front == 165737292u);
+}
+
+/**
+A $(D MersenneTwisterEngine) instantiated with the parameters of the
+original engine $(HTTP en.wikipedia.org/wiki/Mersenne_Twister,
+MT19937-64), generating uniformly-distributed 64-bit numbers with a
+period of 2 to the power of 19937.
+*/
+alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31,
+ 0xb5026f5aa96619e9, 29, 0x5555555555555555, 17,
+ 0x71d67fffeda60000, 37,
+ 0xfff7eee000000000, 43, 6_364_136_223_846_793_005);
+
+///
+@safe unittest
+{
+ // Seed with a constant
+ auto gen = Mt19937_64(12345);
+ auto n = gen.front; // same for each run
+ // Seed with an unpredictable value
+ gen.seed(unpredictableSeed);
+ n = gen.front; // different across runs
+}
+
+@safe nothrow unittest
+{
+ import std.algorithm;
+ import std.range;
+ static assert(isUniformRNG!Mt19937_64);
+ static assert(isUniformRNG!(Mt19937_64, ulong));
+ static assert(isSeedable!Mt19937_64);
+ static assert(isSeedable!(Mt19937_64, ulong));
+ // FIXME: this test demonstrates viably that Mt19937_64
+ // is seedable with an infinite range of `ulong` values
+ // but it's a poor example of how to actually seed the
+ // generator, since it can't cover the full range of
+ // possible seed values. Ideally we need a 64-bit
+ // unpredictable seed to complement the 32-bit one!
+ static assert(isSeedable!(Mt19937_64, typeof(map!((a) => (cast(ulong) unpredictableSeed))(repeat(0)))));
+ Mt19937_64 gen;
+ assert(gen.front == 14514284786278117030uL);
+ popFrontN(gen, 9999);
+ assert(gen.front == 9981545732273789042uL);
+ try { gen.seed(iota(312uL)); } catch (Exception) { assert(false); }
+ assert(gen.front == 14660652410669508483uL);
+ popFrontN(gen, 9999);
+ assert(gen.front == 15956361063660440239uL);
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ import std.exception;
+ import std.range;
+
+ Mt19937 gen;
+
+ assertThrown(gen.seed(map!((a) => unpredictableSeed)(repeat(0, 623))));
+
+ gen.seed(map!((a) => unpredictableSeed)(repeat(0, 624)));
+ //infinite Range
+ gen.seed(map!((a) => unpredictableSeed)(repeat(0)));
+}
+
+@safe pure nothrow unittest
+{
+ uint a, b;
+ {
+ Mt19937 gen;
+ a = gen.front;
+ }
+ {
+ Mt19937 gen;
+ gen.popFront();
+ //popFrontN(gen, 1); // skip 1 element
+ b = gen.front;
+ }
+ assert(a != b);
+}
+
+@safe unittest
+{
+ import std.range;
+ // Check .save works
+ foreach (Type; std.meta.AliasSeq!(Mt19937, Mt19937_64))
+ {
+ auto gen1 = Type(unpredictableSeed);
+ auto gen2 = gen1.save;
+ assert(gen1 == gen2); // Danger, Will Robinson -- no opEquals for MT
+ // Enable next test when RNGs are reference types
+ version (none) { assert(gen1 !is gen2); }
+ assert(gen1.take(100).array() == gen2.take(100).array());
+ }
+}
+
+@safe pure nothrow unittest //11690
+{
+ alias MT(UIntType, uint w) = MersenneTwisterEngine!(UIntType, w, 624, 397, 31,
+ 0x9908b0df, 11, 0xffffffff, 7,
+ 0x9d2c5680, 15,
+ 0xefc60000, 18, 1812433253);
+
+ ulong[] expectedFirstValue = [3499211612uL, 3499211612uL,
+ 171143175841277uL, 1145028863177033374uL];
+
+ ulong[] expected10kValue = [4123659995uL, 4123659995uL,
+ 51991688252792uL, 3031481165133029945uL];
+
+ foreach (i, R; std.meta.AliasSeq!(MT!(uint, 32), MT!(ulong, 32), MT!(ulong, 48), MT!(ulong, 64)))
+ {
+ auto a = R();
+ a.seed(a.defaultSeed); // checks that some alternative paths in `seed` are utilized
+ assert(a.front == expectedFirstValue[i]);
+ a.popFrontN(9999);
+ assert(a.front == expected10kValue[i]);
+ }
+}
+
+
+/**
+ * Xorshift generator using 32bit algorithm.
+ *
+ * Implemented according to $(HTTP www.jstatsoft.org/v08/i14/paper, Xorshift RNGs).
+ * Supporting bits are below, $(D bits) means second parameter of XorshiftEngine.
+ *
+ * $(BOOKTABLE ,
+ * $(TR $(TH bits) $(TH period))
+ * $(TR $(TD 32) $(TD 2^32 - 1))
+ * $(TR $(TD 64) $(TD 2^64 - 1))
+ * $(TR $(TD 96) $(TD 2^96 - 1))
+ * $(TR $(TD 128) $(TD 2^128 - 1))
+ * $(TR $(TD 160) $(TD 2^160 - 1))
+ * $(TR $(TD 192) $(TD 2^192 - 2^32))
+ * )
+ */
+struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType c)
+if (isUnsigned!UIntType)
+{
+ static assert(bits == 32 || bits == 64 || bits == 96 || bits == 128 || bits == 160 || bits == 192,
+ "Xorshift supports only 32, 64, 96, 128, 160 and 192 bit versions. "
+ ~ to!string(bits) ~ " is not supported.");
+
+ public:
+ ///Mark this as a Rng
+ enum bool isUniformRandom = true;
+ /// Always $(D false) (random generators are infinite ranges).
+ enum empty = false;
+ /// Smallest generated value.
+ enum UIntType min = 0;
+ /// Largest generated value.
+ enum UIntType max = UIntType.max;
+
+
+ private:
+ enum size = bits / 32;
+
+ static if (bits == 32)
+ UIntType[size] seeds_ = [2_463_534_242];
+ else static if (bits == 64)
+ UIntType[size] seeds_ = [123_456_789, 362_436_069];
+ else static if (bits == 96)
+ UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629];
+ else static if (bits == 128)
+ UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123];
+ else static if (bits == 160)
+ UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321];
+ else static if (bits == 192)
+ {
+ UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321, 6_615_241];
+ UIntType value_;
+ }
+ else
+ {
+ static assert(false, "Phobos Error: Xorshift has no instantiation rule for "
+ ~ to!string(bits) ~ " bits.");
+ }
+
+
+ public:
+ /**
+ * Constructs a $(D XorshiftEngine) generator seeded with $(D_PARAM x0).
+ */
+ this(UIntType x0) @safe pure nothrow @nogc
+ {
+ seed(x0);
+ }
+
+
+ /**
+ * (Re)seeds the generator.
+ */
+ void seed(UIntType x0) @safe pure nothrow @nogc
+ {
+ // Initialization routine from MersenneTwisterEngine.
+ foreach (i, e; seeds_)
+ seeds_[i] = x0 = cast(UIntType)(1_812_433_253U * (x0 ^ (x0 >> 30)) + i + 1);
+
+ // All seeds must not be 0.
+ sanitizeSeeds(seeds_);
+
+ popFront();
+ }
+
+
+ /**
+ * Returns the current number in the random sequence.
+ */
+ @property
+ UIntType front() const @safe pure nothrow @nogc
+ {
+ static if (bits == 192)
+ return value_;
+ else
+ return seeds_[size - 1];
+ }
+
+
+ /**
+ * Advances the random sequence.
+ */
+ void popFront() @safe pure nothrow @nogc
+ {
+ UIntType temp;
+
+ static if (bits == 32)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ temp = temp ^ (temp >> b);
+ seeds_[0] = temp ^ (temp << c);
+ }
+ else static if (bits == 64)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[1] ^ (seeds_[1] >> c) ^ temp ^ (temp >> b);
+ }
+ else static if (bits == 96)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[2];
+ seeds_[2] = seeds_[2] ^ (seeds_[2] >> c) ^ temp ^ (temp >> b);
+ }
+ else static if (bits == 128)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[2];
+ seeds_[2] = seeds_[3];
+ seeds_[3] = seeds_[3] ^ (seeds_[3] >> c) ^ temp ^ (temp >> b);
+ }
+ else static if (bits == 160)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[2];
+ seeds_[2] = seeds_[3];
+ seeds_[3] = seeds_[4];
+ seeds_[4] = seeds_[4] ^ (seeds_[4] >> c) ^ temp ^ (temp >> b);
+ }
+ else static if (bits == 192)
+ {
+ temp = seeds_[0] ^ (seeds_[0] >> a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[2];
+ seeds_[2] = seeds_[3];
+ seeds_[3] = seeds_[4];
+ seeds_[4] = seeds_[4] ^ (seeds_[4] << c) ^ temp ^ (temp << b);
+ value_ = seeds_[4] + (seeds_[5] += 362_437);
+ }
+ else
+ {
+ static assert(false, "Phobos Error: Xorshift has no popFront() update for "
+ ~ to!string(bits) ~ " bits.");
+ }
+ }
+
+
+ /**
+ * Captures a range state.
+ */
+ @property
+ typeof(this) save() @safe pure nothrow @nogc
+ {
+ return this;
+ }
+
+
+ /**
+ * Compares against $(D_PARAM rhs) for equality.
+ */
+ bool opEquals(ref const XorshiftEngine rhs) const @safe pure nothrow @nogc
+ {
+ return seeds_ == rhs.seeds_;
+ }
+
+
+ private:
+ static void sanitizeSeeds(ref UIntType[size] seeds) @safe pure nothrow @nogc
+ {
+ for (uint i; i < seeds.length; i++)
+ {
+ if (seeds[i] == 0)
+ seeds[i] = i + 1;
+ }
+ }
+
+
+ @safe pure nothrow unittest
+ {
+ static if (size == 4) // Other bits too
+ {
+ UIntType[size] seeds = [1, 0, 0, 4];
+
+ sanitizeSeeds(seeds);
+
+ assert(seeds == [1, 2, 3, 4]);
+ }
+ }
+}
+
+
+/**
+ * Define $(D XorshiftEngine) generators with well-chosen parameters. See each bits examples of "Xorshift RNGs".
+ * $(D Xorshift) is a Xorshift128's alias because 128bits implementation is mostly used.
+ */
+alias Xorshift32 = XorshiftEngine!(uint, 32, 13, 17, 15) ;
+alias Xorshift64 = XorshiftEngine!(uint, 64, 10, 13, 10); /// ditto
+alias Xorshift96 = XorshiftEngine!(uint, 96, 10, 5, 26); /// ditto
+alias Xorshift128 = XorshiftEngine!(uint, 128, 11, 8, 19); /// ditto
+alias Xorshift160 = XorshiftEngine!(uint, 160, 2, 1, 4); /// ditto
+alias Xorshift192 = XorshiftEngine!(uint, 192, 2, 1, 4); /// ditto
+alias Xorshift = Xorshift128; /// ditto
+
+///
+@safe unittest
+{
+ // Seed with a constant
+ auto rnd = Xorshift(1);
+ auto num = rnd.front; // same for each run
+
+ // Seed with an unpredictable value
+ rnd.seed(unpredictableSeed);
+ num = rnd.front; // different across rnd
+}
+
+@safe unittest
+{
+ import std.range;
+ static assert(isForwardRange!Xorshift);
+ static assert(isUniformRNG!Xorshift);
+ static assert(isUniformRNG!(Xorshift, uint));
+ static assert(isSeedable!Xorshift);
+ static assert(isSeedable!(Xorshift, uint));
+
+ // Result from reference implementation.
+ auto checking = [
+ [2463534242UL, 901999875, 3371835698, 2675058524, 1053936272, 3811264849,
+ 472493137, 3856898176, 2131710969, 2312157505],
+ [362436069UL, 2113136921, 19051112, 3010520417, 951284840, 1213972223,
+ 3173832558, 2611145638, 2515869689, 2245824891],
+ [521288629UL, 1950277231, 185954712, 1582725458, 3580567609, 2303633688,
+ 2394948066, 4108622809, 1116800180, 3357585673],
+ [88675123UL, 3701687786, 458299110, 2500872618, 3633119408, 516391518,
+ 2377269574, 2599949379, 717229868, 137866584],
+ [5783321UL, 393427209, 1947109840, 565829276, 1006220149, 971147905,
+ 1436324242, 2800460115, 1484058076, 3823330032],
+ [0UL, 246875399, 3690007200, 1264581005, 3906711041, 1866187943, 2481925219,
+ 2464530826, 1604040631, 3653403911]
+ ];
+
+ alias XorshiftTypes = std.meta.AliasSeq!(Xorshift32, Xorshift64, Xorshift96, Xorshift128, Xorshift160, Xorshift192);
+
+ foreach (I, Type; XorshiftTypes)
+ {
+ Type rnd;
+
+ foreach (e; checking[I])
+ {
+ assert(rnd.front == e);
+ rnd.popFront();
+ }
+ }
+
+ // Check .save works
+ foreach (Type; XorshiftTypes)
+ {
+ auto rnd1 = Type(unpredictableSeed);
+ auto rnd2 = rnd1.save;
+ assert(rnd1 == rnd2);
+ // Enable next test when RNGs are reference types
+ version (none) { assert(rnd1 !is rnd2); }
+ assert(rnd1.take(100).array() == rnd2.take(100).array());
+ }
+}
+
+
+/* A complete list of all pseudo-random number generators implemented in
+ * std.random. This can be used to confirm that a given function or
+ * object is compatible with all the pseudo-random number generators
+ * available. It is enabled only in unittest mode.
+ */
+@safe unittest
+{
+ foreach (Rng; PseudoRngTypes)
+ {
+ static assert(isUniformRNG!Rng);
+ auto rng = Rng(unpredictableSeed);
+ }
+}
+
+
+/**
+A "good" seed for initializing random number engines. Initializing
+with $(D_PARAM unpredictableSeed) makes engines generate different
+random number sequences every run.
+
+Returns:
+A single unsigned integer seed value, different on each successive call
+*/
+@property uint unpredictableSeed() @trusted
+{
+ import core.thread : Thread, getpid, MonoTime;
+ static bool seeded;
+ static MinstdRand0 rand;
+ if (!seeded)
+ {
+ uint threadID = cast(uint) cast(void*) Thread.getThis();
+ rand.seed((getpid() + threadID) ^ cast(uint) MonoTime.currTime.ticks);
+ seeded = true;
+ }
+ rand.popFront();
+ return cast(uint) (MonoTime.currTime.ticks ^ rand.front);
+}
+
+///
+@safe unittest
+{
+ auto rnd = Random(unpredictableSeed);
+ auto n = rnd.front;
+ static assert(is(typeof(n) == uint));
+}
+
+/**
+The "default", "favorite", "suggested" random number generator type on
+the current platform. It is an alias for one of the previously-defined
+generators. You may want to use it if (1) you need to generate some
+nice random numbers, and (2) you don't care for the minutiae of the
+method being used.
+ */
+
+alias Random = Mt19937;
+
+@safe unittest
+{
+ static assert(isUniformRNG!Random);
+ static assert(isUniformRNG!(Random, uint));
+ static assert(isSeedable!Random);
+ static assert(isSeedable!(Random, uint));
+}
+
+/**
+Global random number generator used by various functions in this
+module whenever no generator is specified. It is allocated per-thread
+and initialized to an unpredictable value for each thread.
+
+Returns:
+A singleton instance of the default random number generator
+ */
+@property ref Random rndGen() @safe
+{
+ import std.algorithm.iteration : map;
+ import std.range : repeat;
+
+ static Random result;
+ static bool initialized;
+ if (!initialized)
+ {
+ static if (isSeedable!(Random, typeof(map!((a) => unpredictableSeed)(repeat(0)))))
+ result.seed(map!((a) => unpredictableSeed)(repeat(0)));
+ else
+ result = Random(unpredictableSeed);
+ initialized = true;
+ }
+ return result;
+}
+
+/**
+Generates a number between $(D a) and $(D b). The $(D boundaries)
+parameter controls the shape of the interval (open vs. closed on
+either side). Valid values for $(D boundaries) are $(D "[]"), $(D
+"$(LPAREN)]"), $(D "[$(RPAREN)"), and $(D "()"). The default interval
+is closed to the left and open to the right. The version that does not
+take $(D urng) uses the default generator $(D rndGen).
+
+Params:
+ a = lower bound of the _uniform distribution
+ b = upper bound of the _uniform distribution
+ urng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ A single random variate drawn from the _uniform distribution
+ between $(D a) and $(D b), whose type is the common type of
+ these parameters
+ */
+auto uniform(string boundaries = "[)", T1, T2)
+(T1 a, T2 b)
+if (!is(CommonType!(T1, T2) == void))
+{
+ return uniform!(boundaries, T1, T2, Random)(a, b, rndGen);
+}
+
+///
+@safe unittest
+{
+ auto gen = Random(unpredictableSeed);
+ // Generate an integer in [0, 1023]
+ auto a = uniform(0, 1024, gen);
+ // Generate a float in [0, 1)
+ auto b = uniform(0.0f, 1.0f, gen);
+}
+
+@safe unittest
+{
+ MinstdRand0 gen;
+ foreach (i; 0 .. 20)
+ {
+ auto x = uniform(0.0, 15.0, gen);
+ assert(0 <= x && x < 15);
+ }
+ foreach (i; 0 .. 20)
+ {
+ auto x = uniform!"[]"('a', 'z', gen);
+ assert('a' <= x && x <= 'z');
+ }
+
+ foreach (i; 0 .. 20)
+ {
+ auto x = uniform('a', 'z', gen);
+ assert('a' <= x && x < 'z');
+ }
+
+ foreach (i; 0 .. 20)
+ {
+ immutable ubyte a = 0;
+ immutable ubyte b = 15;
+ auto x = uniform(a, b, gen);
+ assert(a <= x && x < b);
+ }
+}
+
+// Implementation of uniform for floating-point types
+/// ditto
+auto uniform(string boundaries = "[)",
+ T1, T2, UniformRandomNumberGenerator)
+(T1 a, T2 b, ref UniformRandomNumberGenerator urng)
+if (isFloatingPoint!(CommonType!(T1, T2)) && isUniformRNG!UniformRandomNumberGenerator)
+{
+ import std.conv : text;
+ import std.exception : enforce;
+ alias NumberType = Unqual!(CommonType!(T1, T2));
+ static if (boundaries[0] == '(')
+ {
+ import std.math : nextafter;
+ NumberType _a = nextafter(cast(NumberType) a, NumberType.infinity);
+ }
+ else
+ {
+ NumberType _a = a;
+ }
+ static if (boundaries[1] == ')')
+ {
+ import std.math : nextafter;
+ NumberType _b = nextafter(cast(NumberType) b, -NumberType.infinity);
+ }
+ else
+ {
+ NumberType _b = b;
+ }
+ enforce(_a <= _b,
+ text("std.random.uniform(): invalid bounding interval ",
+ boundaries[0], a, ", ", b, boundaries[1]));
+ NumberType result =
+ _a + (_b - _a) * cast(NumberType) (urng.front - urng.min)
+ / (urng.max - urng.min);
+ urng.popFront();
+ return result;
+}
+
+// Implementation of uniform for integral types
+/+ Description of algorithm and suggestion of correctness:
+
+The modulus operator maps an integer to a small, finite space. For instance, `x
+% 3` will map whatever x is into the range [0 .. 3). 0 maps to 0, 1 maps to 1, 2
+maps to 2, 3 maps to 0, and so on infinitely. As long as the integer is
+uniformly chosen from the infinite space of all non-negative integers then `x %
+3` will uniformly fall into that range.
+
+(Non-negative is important in this case because some definitions of modulus,
+namely the one used in computers generally, map negative numbers differently to
+(-3 .. 0]. `uniform` does not use negative number modulus, thus we can safely
+ignore that fact.)
+
+The issue with computers is that integers have a finite space they must fit in,
+and our uniformly chosen random number is picked in that finite space. So, that
+method is not sufficient. You can look at it as the integer space being divided
+into "buckets" and every bucket after the first bucket maps directly into that
+first bucket. `[0, 1, 2]`, `[3, 4, 5]`, ... When integers are finite, then the
+last bucket has the chance to be "incomplete": `[uint.max - 3, uint.max - 2,
+uint.max - 1]`, `[uint.max]` ... (the last bucket only has 1!). The issue here
+is that _every_ bucket maps _completely_ to the first bucket except for that
+last one. The last one doesn't have corresponding mappings to 1 or 2, in this
+case, which makes it unfair.
+
+So, the answer is to simply "reroll" if you're in that last bucket, since it's
+the only unfair one. Eventually you'll roll into a fair bucket. Simply, instead
+of the meaning of the last bucket being "maps to `[0]`", it changes to "maps to
+`[0, 1, 2]`", which is precisely what we want.
+
+To generalize, `upperDist` represents the size of our buckets (and, thus, the
+exclusive upper bound for our desired uniform number). `rnum` is a uniformly
+random number picked from the space of integers that a computer can hold (we'll
+say `UpperType` represents that type).
+
+We'll first try to do the mapping into the first bucket by doing `offset = rnum
+% upperDist`. We can figure out the position of the front of the bucket we're in
+by `bucketFront = rnum - offset`.
+
+If we start at `UpperType.max` and walk backwards `upperDist - 1` spaces, then
+the space we land on is the last acceptable position where a full bucket can
+fit:
+
+```
+ bucketFront UpperType.max
+ v v
+[..., 0, 1, 2, ..., upperDist - 1]
+ ^~~ upperDist - 1 ~~^
+```
+
+If the bucket starts any later, then it must have lost at least one number and
+at least that number won't be represented fairly.
+
+```
+ bucketFront UpperType.max
+ v v
+[..., upperDist - 1, 0, 1, 2, ..., upperDist - 2]
+ ^~~~~~~~ upperDist - 1 ~~~~~~~^
+```
+
+Hence, our condition to reroll is
+`bucketFront > (UpperType.max - (upperDist - 1))`
++/
+auto uniform(string boundaries = "[)", T1, T2, RandomGen)
+(T1 a, T2 b, ref RandomGen rng)
+if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) &&
+ isUniformRNG!RandomGen)
+{
+ import std.conv : text, unsigned;
+ import std.exception : enforce;
+ alias ResultType = Unqual!(CommonType!(T1, T2));
+ static if (boundaries[0] == '(')
+ {
+ enforce(a < ResultType.max,
+ text("std.random.uniform(): invalid left bound ", a));
+ ResultType lower = cast(ResultType) (a + 1);
+ }
+ else
+ {
+ ResultType lower = a;
+ }
+
+ static if (boundaries[1] == ']')
+ {
+ enforce(lower <= b,
+ text("std.random.uniform(): invalid bounding interval ",
+ boundaries[0], a, ", ", b, boundaries[1]));
+ /* Cannot use this next optimization with dchar, as dchar
+ * only partially uses its full bit range
+ */
+ static if (!is(ResultType == dchar))
+ {
+ if (b == ResultType.max && lower == ResultType.min)
+ {
+ // Special case - all bits are occupied
+ return std.random.uniform!ResultType(rng);
+ }
+ }
+ auto upperDist = unsigned(b - lower) + 1u;
+ }
+ else
+ {
+ enforce(lower < b,
+ text("std.random.uniform(): invalid bounding interval ",
+ boundaries[0], a, ", ", b, boundaries[1]));
+ auto upperDist = unsigned(b - lower);
+ }
+
+ assert(upperDist != 0);
+
+ alias UpperType = typeof(upperDist);
+ static assert(UpperType.min == 0);
+
+ UpperType offset, rnum, bucketFront;
+ do
+ {
+ rnum = uniform!UpperType(rng);
+ offset = rnum % upperDist;
+ bucketFront = rnum - offset;
+ } // while we're in an unfair bucket...
+ while (bucketFront > (UpperType.max - (upperDist - 1)));
+
+ return cast(ResultType)(lower + offset);
+}
+
+@safe unittest
+{
+ import std.conv : to;
+ auto gen = Mt19937(unpredictableSeed);
+ static assert(isForwardRange!(typeof(gen)));
+
+ auto a = uniform(0, 1024, gen);
+ assert(0 <= a && a <= 1024);
+ auto b = uniform(0.0f, 1.0f, gen);
+ assert(0 <= b && b < 1, to!string(b));
+ auto c = uniform(0.0, 1.0);
+ assert(0 <= c && c < 1);
+
+ foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
+ int, uint, long, ulong, float, double, real))
+ {
+ T lo = 0, hi = 100;
+
+ // Try tests with each of the possible bounds
+ {
+ T init = uniform(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+ {
+ T init = uniform!"[)"(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+ {
+ T init = uniform!"(]"(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+ {
+ T init = uniform!"()"(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+ {
+ T init = uniform!"[]"(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+
+ /* Test case with closed boundaries covering whole range
+ * of integral type
+ */
+ static if (isIntegral!T || isSomeChar!T)
+ {
+ foreach (immutable _; 0 .. 100)
+ {
+ auto u = uniform!"[]"(T.min, T.max);
+ static assert(is(typeof(u) == T));
+ assert(T.min <= u, "Lower bound violation for uniform!\"[]\" with " ~ T.stringof);
+ assert(u <= T.max, "Upper bound violation for uniform!\"[]\" with " ~ T.stringof);
+ }
+ }
+ }
+
+ auto reproRng = Xorshift(239842);
+
+ foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short,
+ ushort, int, uint, long, ulong))
+ {
+ T lo = T.min + 10, hi = T.max - 10;
+ T init = uniform(lo, hi, reproRng);
+ size_t i = 50;
+ while (--i && uniform(lo, hi, reproRng) == init) {}
+ assert(i > 0);
+ }
+
+ {
+ bool sawLB = false, sawUB = false;
+ foreach (i; 0 .. 50)
+ {
+ auto x = uniform!"[]"('a', 'd', reproRng);
+ if (x == 'a') sawLB = true;
+ if (x == 'd') sawUB = true;
+ assert('a' <= x && x <= 'd');
+ }
+ assert(sawLB && sawUB);
+ }
+
+ {
+ bool sawLB = false, sawUB = false;
+ foreach (i; 0 .. 50)
+ {
+ auto x = uniform('a', 'd', reproRng);
+ if (x == 'a') sawLB = true;
+ if (x == 'c') sawUB = true;
+ assert('a' <= x && x < 'd');
+ }
+ assert(sawLB && sawUB);
+ }
+
+ {
+ bool sawLB = false, sawUB = false;
+ foreach (i; 0 .. 50)
+ {
+ immutable int lo = -2, hi = 2;
+ auto x = uniform!"()"(lo, hi, reproRng);
+ if (x == (lo+1)) sawLB = true;
+ if (x == (hi-1)) sawUB = true;
+ assert(lo < x && x < hi);
+ }
+ assert(sawLB && sawUB);
+ }
+
+ {
+ bool sawLB = false, sawUB = false;
+ foreach (i; 0 .. 50)
+ {
+ immutable ubyte lo = 0, hi = 5;
+ auto x = uniform(lo, hi, reproRng);
+ if (x == lo) sawLB = true;
+ if (x == (hi-1)) sawUB = true;
+ assert(lo <= x && x < hi);
+ }
+ assert(sawLB && sawUB);
+ }
+
+ {
+ foreach (i; 0 .. 30)
+ {
+ assert(i == uniform(i, i+1, reproRng));
+ }
+ }
+}
+
+/**
+Generates a uniformly-distributed number in the range $(D [T.min,
+T.max]) for any integral or character type $(D T). If no random
+number generator is passed, uses the default $(D rndGen).
+
+Params:
+ urng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ Random variate drawn from the _uniform distribution across all
+ possible values of the integral or character type $(D T).
+ */
+auto uniform(T, UniformRandomNumberGenerator)
+(ref UniformRandomNumberGenerator urng)
+if (!is(T == enum) && (isIntegral!T || isSomeChar!T) && isUniformRNG!UniformRandomNumberGenerator)
+{
+ /* dchar does not use its full bit range, so we must
+ * revert to the uniform with specified bounds
+ */
+ static if (is(T == dchar))
+ {
+ return uniform!"[]"(T.min, T.max);
+ }
+ else
+ {
+ auto r = urng.front;
+ urng.popFront();
+ static if (T.sizeof <= r.sizeof)
+ {
+ return cast(T) r;
+ }
+ else
+ {
+ static assert(T.sizeof == 8 && r.sizeof == 4);
+ T r1 = urng.front | (cast(T) r << 32);
+ urng.popFront();
+ return r1;
+ }
+ }
+}
+
+/// Ditto
+auto uniform(T)()
+if (!is(T == enum) && (isIntegral!T || isSomeChar!T))
+{
+ return uniform!T(rndGen);
+}
+
+@safe unittest
+{
+ foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
+ int, uint, long, ulong))
+ {
+ T init = uniform!T();
+ size_t i = 50;
+ while (--i && uniform!T() == init) {}
+ assert(i > 0);
+
+ foreach (immutable _; 0 .. 100)
+ {
+ auto u = uniform!T();
+ static assert(is(typeof(u) == T));
+ assert(T.min <= u, "Lower bound violation for uniform!" ~ T.stringof);
+ assert(u <= T.max, "Upper bound violation for uniform!" ~ T.stringof);
+ }
+ }
+}
+
+/**
+Returns a uniformly selected member of enum $(D E). If no random number
+generator is passed, uses the default $(D rndGen).
+
+Params:
+ urng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ Random variate drawn with equal probability from any
+ of the possible values of the enum $(D E).
+ */
+auto uniform(E, UniformRandomNumberGenerator)
+(ref UniformRandomNumberGenerator urng)
+if (is(E == enum) && isUniformRNG!UniformRandomNumberGenerator)
+{
+ static immutable E[EnumMembers!E.length] members = [EnumMembers!E];
+ return members[std.random.uniform(0, members.length, urng)];
+}
+
+/// Ditto
+auto uniform(E)()
+if (is(E == enum))
+{
+ return uniform!E(rndGen);
+}
+
+///
+@safe unittest
+{
+ enum Fruit { apple, mango, pear }
+ auto randFruit = uniform!Fruit();
+}
+
+@safe unittest
+{
+ enum Fruit { Apple = 12, Mango = 29, Pear = 72 }
+ foreach (_; 0 .. 100)
+ {
+ foreach (f; [uniform!Fruit(), rndGen.uniform!Fruit()])
+ {
+ assert(f == Fruit.Apple || f == Fruit.Mango || f == Fruit.Pear);
+ }
+ }
+}
+
+/**
+ * Generates a uniformly-distributed floating point number of type
+ * $(D T) in the range [0, 1$(RPAREN). If no random number generator is
+ * specified, the default RNG $(D rndGen) will be used as the source
+ * of randomness.
+ *
+ * $(D uniform01) offers a faster generation of random variates than
+ * the equivalent $(D uniform!"[$(RPAREN)"(0.0, 1.0)) and so may be preferred
+ * for some applications.
+ *
+ * Params:
+ * rng = (optional) random number generator to use;
+ * if not specified, defaults to $(D rndGen)
+ *
+ * Returns:
+ * Floating-point random variate of type $(D T) drawn from the _uniform
+ * distribution across the half-open interval [0, 1$(RPAREN).
+ *
+ */
+T uniform01(T = double)()
+if (isFloatingPoint!T)
+{
+ return uniform01!T(rndGen);
+}
+
+/// ditto
+T uniform01(T = double, UniformRNG)(ref UniformRNG rng)
+if (isFloatingPoint!T && isUniformRNG!UniformRNG)
+out (result)
+{
+ assert(0 <= result);
+ assert(result < 1);
+}
+body
+{
+ alias R = typeof(rng.front);
+ static if (isIntegral!R)
+ {
+ enum T factor = 1 / (T(1) + rng.max - rng.min);
+ }
+ else static if (isFloatingPoint!R)
+ {
+ enum T factor = 1 / (rng.max - rng.min);
+ }
+ else
+ {
+ static assert(false);
+ }
+
+ while (true)
+ {
+ immutable T u = (rng.front - rng.min) * factor;
+ rng.popFront();
+
+ import core.stdc.limits : CHAR_BIT; // CHAR_BIT is always 8
+ static if (isIntegral!R && T.mant_dig >= (CHAR_BIT * R.sizeof))
+ {
+ /* If RNG variates are integral and T has enough precision to hold
+ * R without loss, we're guaranteed by the definition of factor
+ * that precisely u < 1.
+ */
+ return u;
+ }
+ else
+ {
+ /* Otherwise we have to check whether u is beyond the assumed range
+ * because of the loss of precision, or for another reason, a
+ * floating-point RNG can return a variate that is exactly equal to
+ * its maximum.
+ */
+ if (u < 1)
+ {
+ return u;
+ }
+ }
+ }
+
+ // Shouldn't ever get here.
+ assert(false);
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (UniformRNG; PseudoRngTypes)
+ {
+
+ foreach (T; std.meta.AliasSeq!(float, double, real))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ UniformRNG rng = UniformRNG(unpredictableSeed);
+
+ auto a = uniform01();
+ assert(is(typeof(a) == double));
+ assert(0 <= a && a < 1);
+
+ auto b = uniform01(rng);
+ assert(is(typeof(a) == double));
+ assert(0 <= b && b < 1);
+
+ auto c = uniform01!T();
+ assert(is(typeof(c) == T));
+ assert(0 <= c && c < 1);
+
+ auto d = uniform01!T(rng);
+ assert(is(typeof(d) == T));
+ assert(0 <= d && d < 1);
+
+ T init = uniform01!T(rng);
+ size_t i = 50;
+ while (--i && uniform01!T(rng) == init) {}
+ assert(i > 0);
+ assert(i < 50);
+ }();
+ }
+}
+
+/**
+Generates a uniform probability distribution of size $(D n), i.e., an
+array of size $(D n) of positive numbers of type $(D F) that sum to
+$(D 1). If $(D useThis) is provided, it is used as storage.
+ */
+F[] uniformDistribution(F = double)(size_t n, F[] useThis = null)
+if (isFloatingPoint!F)
+{
+ import std.numeric : normalize;
+ useThis.length = n;
+ foreach (ref e; useThis)
+ {
+ e = uniform(0.0, 1);
+ }
+ normalize(useThis);
+ return useThis;
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ import std.math;
+ static assert(is(CommonType!(double, int) == double));
+ auto a = uniformDistribution(5);
+ assert(a.length == 5);
+ assert(approxEqual(reduce!"a + b"(a), 1));
+ a = uniformDistribution(10, a);
+ assert(a.length == 10);
+ assert(approxEqual(reduce!"a + b"(a), 1));
+}
+
+/**
+Returns a random, uniformly chosen, element `e` from the supplied
+$(D Range range). If no random number generator is passed, the default
+`rndGen` is used.
+
+Params:
+ range = a random access range that has the `length` property defined
+ urng = (optional) random number generator to use;
+ if not specified, defaults to `rndGen`
+
+Returns:
+ A single random element drawn from the `range`. If it can, it will
+ return a `ref` to the $(D range element), otherwise it will return
+ a copy.
+ */
+auto ref choice(Range, RandomGen = Random)(auto ref Range range,
+ ref RandomGen urng = rndGen)
+if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen)
+{
+ assert(range.length > 0,
+ __PRETTY_FUNCTION__ ~ ": invalid Range supplied. Range cannot be empty");
+
+ return range[uniform(size_t(0), $, urng)];
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+
+ auto array = [1, 2, 3, 4, 5];
+ auto elem = choice(array);
+
+ assert(canFind(array, elem),
+ "Choice did not return a valid element from the given Range");
+
+ auto urng = Random(unpredictableSeed);
+ elem = choice(array, urng);
+
+ assert(canFind(array, elem),
+ "Choice did not return a valid element from the given Range");
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+
+ class MyTestClass
+ {
+ int x;
+
+ this(int x)
+ {
+ this.x = x;
+ }
+ }
+
+ MyTestClass[] testClass;
+ foreach (i; 0 .. 5)
+ {
+ testClass ~= new MyTestClass(i);
+ }
+
+ auto elem = choice(testClass);
+
+ assert(canFind!((ref MyTestClass a, ref MyTestClass b) => a.x == b.x)(testClass, elem),
+ "Choice did not return a valid element from the given Range");
+}
+
+@system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.algorithm.searching : canFind;
+
+ auto array = [1, 2, 3, 4, 5];
+ auto elemAddr = &choice(array);
+
+ assert(array.map!((ref e) => &e).canFind(elemAddr),
+ "Choice did not return a ref to an element from the given Range");
+ assert(array.canFind(*(cast(int *)(elemAddr))),
+ "Choice did not return a valid element from the given Range");
+}
+
+/**
+Shuffles elements of $(D r) using $(D gen) as a shuffler. $(D r) must be
+a random-access range with length. If no RNG is specified, $(D rndGen)
+will be used.
+
+Params:
+ r = random-access range whose elements are to be shuffled
+ gen = (optional) random number generator to use; if not
+ specified, defaults to $(D rndGen)
+ */
+
+void randomShuffle(Range, RandomGen)(Range r, ref RandomGen gen)
+if (isRandomAccessRange!Range && isUniformRNG!RandomGen)
+{
+ return partialShuffle!(Range, RandomGen)(r, r.length, gen);
+}
+
+/// ditto
+void randomShuffle(Range)(Range r)
+if (isRandomAccessRange!Range)
+{
+ return randomShuffle(r, rndGen);
+}
+
+@safe unittest
+{
+ import std.algorithm.sorting : sort;
+ foreach (RandomGen; PseudoRngTypes)
+ {
+ // Also tests partialShuffle indirectly.
+ auto a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ auto b = a.dup;
+ auto gen = RandomGen(unpredictableSeed);
+ randomShuffle(a, gen);
+ sort(a);
+ assert(a == b);
+ randomShuffle(a);
+ sort(a);
+ assert(a == b);
+ }
+}
+
+/**
+Partially shuffles the elements of $(D r) such that upon returning $(D r[0 .. n])
+is a random subset of $(D r) and is randomly ordered. $(D r[n .. r.length])
+will contain the elements not in $(D r[0 .. n]). These will be in an undefined
+order, but will not be random in the sense that their order after
+$(D partialShuffle) returns will not be independent of their order before
+$(D partialShuffle) was called.
+
+$(D r) must be a random-access range with length. $(D n) must be less than
+or equal to $(D r.length). If no RNG is specified, $(D rndGen) will be used.
+
+Params:
+ r = random-access range whose elements are to be shuffled
+ n = number of elements of $(D r) to shuffle (counting from the beginning);
+ must be less than $(D r.length)
+ gen = (optional) random number generator to use; if not
+ specified, defaults to $(D rndGen)
+*/
+void partialShuffle(Range, RandomGen)(Range r, in size_t n, ref RandomGen gen)
+if (isRandomAccessRange!Range && isUniformRNG!RandomGen)
+{
+ import std.algorithm.mutation : swapAt;
+ import std.exception : enforce;
+ enforce(n <= r.length, "n must be <= r.length for partialShuffle.");
+ foreach (i; 0 .. n)
+ {
+ r.swapAt(i, uniform(i, r.length, gen));
+ }
+}
+
+/// ditto
+void partialShuffle(Range)(Range r, in size_t n)
+if (isRandomAccessRange!Range)
+{
+ return partialShuffle(r, n, rndGen);
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ foreach (RandomGen; PseudoRngTypes)
+ {
+ auto a = [0, 1, 1, 2, 3];
+ auto b = a.dup;
+
+ // Pick a fixed seed so that the outcome of the statistical
+ // test below is deterministic.
+ auto gen = RandomGen(12345);
+
+ // NUM times, pick LEN elements from the array at random.
+ immutable int LEN = 2;
+ immutable int NUM = 750;
+ int[][] chk;
+ foreach (step; 0 .. NUM)
+ {
+ partialShuffle(a, LEN, gen);
+ chk ~= a[0 .. LEN].dup;
+ }
+
+ // Check that each possible a[0 .. LEN] was produced at least once.
+ // For a perfectly random RandomGen, the probability that each
+ // particular combination failed to appear would be at most
+ // 0.95 ^^ NUM which is approximately 1,962e-17.
+ // As long as hardware failure (e.g. bit flip) probability
+ // is higher, we are fine with this unittest.
+ sort(chk);
+ assert(equal(uniq(chk), [ [0,1], [0,2], [0,3],
+ [1,0], [1,1], [1,2], [1,3],
+ [2,0], [2,1], [2,3],
+ [3,0], [3,1], [3,2], ]));
+
+ // Check that all the elements are still there.
+ sort(a);
+ assert(equal(a, b));
+ }
+}
+
+/**
+Rolls a dice with relative probabilities stored in $(D
+proportions). Returns the index in $(D proportions) that was chosen.
+
+Params:
+ rnd = (optional) random number generator to use; if not
+ specified, defaults to $(D rndGen)
+ proportions = forward range or list of individual values
+ whose elements correspond to the probabilities
+ with which to choose the corresponding index
+ value
+
+Returns:
+ Random variate drawn from the index values
+ [0, ... $(D proportions.length) - 1], with the probability
+ of getting an individual index value $(D i) being proportional to
+ $(D proportions[i]).
+*/
+size_t dice(Rng, Num)(ref Rng rnd, Num[] proportions...)
+if (isNumeric!Num && isForwardRange!Rng)
+{
+ return diceImpl(rnd, proportions);
+}
+
+/// Ditto
+size_t dice(R, Range)(ref R rnd, Range proportions)
+if (isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range)
+{
+ return diceImpl(rnd, proportions);
+}
+
+/// Ditto
+size_t dice(Range)(Range proportions)
+if (isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range)
+{
+ return diceImpl(rndGen, proportions);
+}
+
+/// Ditto
+size_t dice(Num)(Num[] proportions...)
+if (isNumeric!Num)
+{
+ return diceImpl(rndGen, proportions);
+}
+
+///
+@safe unittest
+{
+ auto x = dice(0.5, 0.5); // x is 0 or 1 in equal proportions
+ auto y = dice(50, 50); // y is 0 or 1 in equal proportions
+ auto z = dice(70, 20, 10); // z is 0 70% of the time, 1 20% of the time,
+ // and 2 10% of the time
+}
+
+private size_t diceImpl(Rng, Range)(ref Rng rng, scope Range proportions)
+if (isForwardRange!Range && isNumeric!(ElementType!Range) && isForwardRange!Rng)
+in
+{
+ import std.algorithm.searching : all;
+ assert(proportions.save.all!"a >= 0");
+}
+body
+{
+ import std.algorithm.iteration : reduce;
+ import std.exception : enforce;
+ double sum = reduce!"a + b"(0.0, proportions.save);
+ enforce(sum > 0, "Proportions in a dice cannot sum to zero");
+ immutable point = uniform(0.0, sum, rng);
+ assert(point < sum);
+ auto mass = 0.0;
+
+ size_t i = 0;
+ foreach (e; proportions)
+ {
+ mass += e;
+ if (point < mass) return i;
+ i++;
+ }
+ // this point should not be reached
+ assert(false);
+}
+
+@safe unittest
+{
+ auto rnd = Random(unpredictableSeed);
+ auto i = dice(rnd, 0.0, 100.0);
+ assert(i == 1);
+ i = dice(rnd, 100.0, 0.0);
+ assert(i == 0);
+
+ i = dice(100U, 0U);
+ assert(i == 0);
+}
+
+/**
+Covers a given range $(D r) in a random manner, i.e. goes through each
+element of $(D r) once and only once, just in a random order. $(D r)
+must be a random-access range with length.
+
+If no random number generator is passed to $(D randomCover), the
+thread-global RNG rndGen will be used internally.
+
+Params:
+ r = random-access range to cover
+ rng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ Range whose elements consist of the elements of $(D r),
+ in random order. Will be a forward range if both $(D r) and
+ $(D rng) are forward ranges, an input range otherwise.
+
+Example:
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
+foreach (e; randomCover(a))
+{
+ writeln(e);
+}
+----
+
+$(B WARNING:) If an alternative RNG is desired, it is essential for this
+to be a $(I new) RNG seeded in an unpredictable manner. Passing it a RNG
+used elsewhere in the program will result in unintended correlations,
+due to the current implementation of RNGs as value types.
+
+Example:
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
+foreach (e; randomCover(a, Random(unpredictableSeed))) // correct!
+{
+ writeln(e);
+}
+
+foreach (e; randomCover(a, rndGen)) // DANGEROUS!! rndGen gets copied by value
+{
+ writeln(e);
+}
+
+foreach (e; randomCover(a, rndGen)) // ... so this second random cover
+{ // will output the same sequence as
+ writeln(e); // the previous one.
+}
+----
+ */
+struct RandomCover(Range, UniformRNG = void)
+if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
+{
+ private Range _input;
+ private bool[] _chosen;
+ private size_t _current;
+ private size_t _alreadyChosen = 0;
+ private bool _isEmpty = false;
+
+ static if (is(UniformRNG == void))
+ {
+ this(Range input)
+ {
+ _input = input;
+ _chosen.length = _input.length;
+ if (_input.empty)
+ {
+ _isEmpty = true;
+ }
+ else
+ {
+ _current = uniform(0, _chosen.length);
+ }
+ }
+ }
+ else
+ {
+ private UniformRNG _rng;
+
+ this(Range input, ref UniformRNG rng)
+ {
+ _input = input;
+ _rng = rng;
+ _chosen.length = _input.length;
+ if (_input.empty)
+ {
+ _isEmpty = true;
+ }
+ else
+ {
+ _current = uniform(0, _chosen.length, rng);
+ }
+ }
+
+ this(Range input, UniformRNG rng)
+ {
+ this(input, rng);
+ }
+ }
+
+ static if (hasLength!Range)
+ {
+ @property size_t length()
+ {
+ return _input.length - _alreadyChosen;
+ }
+ }
+
+ @property auto ref front()
+ {
+ assert(!_isEmpty);
+ return _input[_current];
+ }
+
+ void popFront()
+ {
+ assert(!_isEmpty);
+
+ size_t k = _input.length - _alreadyChosen - 1;
+ if (k == 0)
+ {
+ _isEmpty = true;
+ ++_alreadyChosen;
+ return;
+ }
+
+ size_t i;
+ foreach (e; _input)
+ {
+ if (_chosen[i] || i == _current) { ++i; continue; }
+ // Roll a dice with k faces
+ static if (is(UniformRNG == void))
+ {
+ auto chooseMe = uniform(0, k) == 0;
+ }
+ else
+ {
+ auto chooseMe = uniform(0, k, _rng) == 0;
+ }
+ assert(k > 1 || chooseMe);
+ if (chooseMe)
+ {
+ _chosen[_current] = true;
+ _current = i;
+ ++_alreadyChosen;
+ return;
+ }
+ --k;
+ ++i;
+ }
+ }
+
+ static if (isForwardRange!UniformRNG)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ ret._rng = _rng.save;
+ return ret;
+ }
+ }
+
+ @property bool empty() { return _isEmpty; }
+}
+
+/// Ditto
+auto randomCover(Range, UniformRNG)(Range r, auto ref UniformRNG rng)
+if (isRandomAccessRange!Range && isUniformRNG!UniformRNG)
+{
+ return RandomCover!(Range, UniformRNG)(r, rng);
+}
+
+/// Ditto
+auto randomCover(Range)(Range r)
+if (isRandomAccessRange!Range)
+{
+ return RandomCover!(Range, void)(r);
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ import std.conv;
+ int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
+ int[] c;
+ foreach (UniformRNG; std.meta.AliasSeq!(void, PseudoRngTypes))
+ {
+ static if (is(UniformRNG == void))
+ {
+ auto rc = randomCover(a);
+ static assert(isInputRange!(typeof(rc)));
+ static assert(!isForwardRange!(typeof(rc)));
+ }
+ else
+ {
+ auto rng = UniformRNG(unpredictableSeed);
+ auto rc = randomCover(a, rng);
+ static assert(isForwardRange!(typeof(rc)));
+ // check for constructor passed a value-type RNG
+ auto rc2 = RandomCover!(int[], UniformRNG)(a, UniformRNG(unpredictableSeed));
+ static assert(isForwardRange!(typeof(rc2)));
+ auto rcEmpty = randomCover(c, rng);
+ assert(rcEmpty.length == 0);
+ }
+
+ int[] b = new int[9];
+ uint i;
+ foreach (e; rc)
+ {
+ //writeln(e);
+ b[i++] = e;
+ }
+ sort(b);
+ assert(a == b, text(b));
+ }
+}
+
+@safe unittest
+{
+ // Bugzilla 12589
+ int[] r = [];
+ auto rc = randomCover(r);
+ assert(rc.length == 0);
+ assert(rc.empty);
+
+ // Bugzilla 16724
+ import std.range : iota;
+ auto range = iota(10);
+ auto randy = range.randomCover;
+
+ for (int i=1; i <= range.length; i++)
+ {
+ randy.popFront;
+ assert(randy.length == range.length - i);
+ }
+}
+
+// RandomSample
+/**
+Selects a random subsample out of $(D r), containing exactly $(D n)
+elements. The order of elements is the same as in the original
+range. The total length of $(D r) must be known. If $(D total) is
+passed in, the total number of sample is considered to be $(D
+total). Otherwise, $(D RandomSample) uses $(D r.length).
+
+Params:
+ r = range to sample from
+ n = number of elements to include in the sample;
+ must be less than or equal to the total number
+ of elements in $(D r) and/or the parameter
+ $(D total) (if provided)
+ total = (semi-optional) number of elements of $(D r)
+ from which to select the sample (counting from
+ the beginning); must be less than or equal to
+ the total number of elements in $(D r) itself.
+ May be omitted if $(D r) has the $(D .length)
+ property and the sample is to be drawn from
+ all elements of $(D r).
+ rng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ Range whose elements consist of a randomly selected subset of
+ the elements of $(D r), in the same order as these elements
+ appear in $(D r) itself. Will be a forward range if both $(D r)
+ and $(D rng) are forward ranges, an input range otherwise.
+
+$(D RandomSample) implements Jeffrey Scott Vitter's Algorithm D
+(see Vitter $(HTTP dx.doi.org/10.1145/358105.893, 1984), $(HTTP
+dx.doi.org/10.1145/23002.23003, 1987)), which selects a sample
+of size $(D n) in O(n) steps and requiring O(n) random variates,
+regardless of the size of the data being sampled. The exception
+to this is if traversing k elements on the input range is itself
+an O(k) operation (e.g. when sampling lines from an input file),
+in which case the sampling calculation will inevitably be of
+O(total).
+
+RandomSample will throw an exception if $(D total) is verifiably
+less than the total number of elements available in the input,
+or if $(D n > total).
+
+If no random number generator is passed to $(D randomSample), the
+thread-global RNG rndGen will be used internally.
+
+Example:
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
+// Print 5 random elements picked off from a
+foreach (e; randomSample(a, 5))
+{
+ writeln(e);
+}
+----
+
+$(B WARNING:) If an alternative RNG is desired, it is essential for this
+to be a $(I new) RNG seeded in an unpredictable manner. Passing it a RNG
+used elsewhere in the program will result in unintended correlations,
+due to the current implementation of RNGs as value types.
+
+Example:
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
+foreach (e; randomSample(a, 5, Random(unpredictableSeed))) // correct!
+{
+ writeln(e);
+}
+
+foreach (e; randomSample(a, 5, rndGen)) // DANGEROUS!! rndGen gets
+{ // copied by value
+ writeln(e);
+}
+
+foreach (e; randomSample(a, 5, rndGen)) // ... so this second random
+{ // sample will select the same
+ writeln(e); // values as the previous one.
+}
+----
+*/
+struct RandomSample(Range, UniformRNG = void)
+if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
+{
+ private size_t _available, _toSelect;
+ private enum ushort _alphaInverse = 13; // Vitter's recommended value.
+ private double _Vprime;
+ private Range _input;
+ private size_t _index;
+ private enum Skip { None, A, D }
+ private Skip _skip = Skip.None;
+
+ // If we're using the default thread-local random number generator then
+ // we shouldn't store a copy of it here. UniformRNG == void is a sentinel
+ // for this. If we're using a user-specified generator then we have no
+ // choice but to store a copy.
+ static if (is(UniformRNG == void))
+ {
+ static if (hasLength!Range)
+ {
+ this(Range input, size_t howMany)
+ {
+ _input = input;
+ initialize(howMany, input.length);
+ }
+ }
+
+ this(Range input, size_t howMany, size_t total)
+ {
+ _input = input;
+ initialize(howMany, total);
+ }
+ }
+ else
+ {
+ UniformRNG _rng;
+
+ static if (hasLength!Range)
+ {
+ this(Range input, size_t howMany, ref UniformRNG rng)
+ {
+ _rng = rng;
+ _input = input;
+ initialize(howMany, input.length);
+ }
+
+ this(Range input, size_t howMany, UniformRNG rng)
+ {
+ this(input, howMany, rng);
+ }
+ }
+
+ this(Range input, size_t howMany, size_t total, ref UniformRNG rng)
+ {
+ _rng = rng;
+ _input = input;
+ initialize(howMany, total);
+ }
+
+ this(Range input, size_t howMany, size_t total, UniformRNG rng)
+ {
+ this(input, howMany, total, rng);
+ }
+ }
+
+ private void initialize(size_t howMany, size_t total)
+ {
+ import std.conv : text;
+ import std.exception : enforce;
+ _available = total;
+ _toSelect = howMany;
+ enforce(_toSelect <= _available,
+ text("RandomSample: cannot sample ", _toSelect,
+ " items when only ", _available, " are available"));
+ static if (hasLength!Range)
+ {
+ enforce(_available <= _input.length,
+ text("RandomSample: specified ", _available,
+ " items as available when input contains only ",
+ _input.length));
+ }
+ }
+
+ private void initializeFront()
+ {
+ assert(_skip == Skip.None);
+ // We can save ourselves a random variate by checking right
+ // at the beginning if we should use Algorithm A.
+ if ((_alphaInverse * _toSelect) > _available)
+ {
+ _skip = Skip.A;
+ }
+ else
+ {
+ _skip = Skip.D;
+ _Vprime = newVprime(_toSelect);
+ }
+ prime();
+ }
+
+/**
+ Range primitives.
+*/
+ @property bool empty() const
+ {
+ return _toSelect == 0;
+ }
+
+/// Ditto
+ @property auto ref front()
+ {
+ assert(!empty);
+ // The first sample point must be determined here to avoid
+ // having it always correspond to the first element of the
+ // input. The rest of the sample points are determined each
+ // time we call popFront().
+ if (_skip == Skip.None)
+ {
+ initializeFront();
+ }
+ return _input.front;
+ }
+
+/// Ditto
+ void popFront()
+ {
+ // First we need to check if the sample has
+ // been initialized in the first place.
+ if (_skip == Skip.None)
+ {
+ initializeFront();
+ }
+
+ _input.popFront();
+ --_available;
+ --_toSelect;
+ ++_index;
+ prime();
+ }
+
+/// Ditto
+ static if (isForwardRange!Range && isForwardRange!UniformRNG)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ ret._rng = _rng.save;
+ return ret;
+ }
+ }
+
+/// Ditto
+ @property size_t length()
+ {
+ return _toSelect;
+ }
+
+/**
+Returns the index of the visited record.
+ */
+ @property size_t index()
+ {
+ if (_skip == Skip.None)
+ {
+ initializeFront();
+ }
+ return _index;
+ }
+
+ private size_t skip()
+ {
+ assert(_skip != Skip.None);
+
+ // Step D1: if the number of points still to select is greater
+ // than a certain proportion of the remaining data points, i.e.
+ // if n >= alpha * N where alpha = 1/13, we carry out the
+ // sampling with Algorithm A.
+ if (_skip == Skip.A)
+ {
+ return skipA();
+ }
+ else if ((_alphaInverse * _toSelect) > _available)
+ {
+ // We shouldn't get here unless the current selected
+ // algorithm is D.
+ assert(_skip == Skip.D);
+ _skip = Skip.A;
+ return skipA();
+ }
+ else
+ {
+ assert(_skip == Skip.D);
+ return skipD();
+ }
+ }
+
+/*
+Vitter's Algorithm A, used when the ratio of needed sample values
+to remaining data values is sufficiently large.
+*/
+ private size_t skipA()
+ {
+ size_t s;
+ double v, quot, top;
+
+ if (_toSelect == 1)
+ {
+ static if (is(UniformRNG == void))
+ {
+ s = uniform(0, _available);
+ }
+ else
+ {
+ s = uniform(0, _available, _rng);
+ }
+ }
+ else
+ {
+ v = 0;
+ top = _available - _toSelect;
+ quot = top / _available;
+
+ static if (is(UniformRNG == void))
+ {
+ v = uniform!"()"(0.0, 1.0);
+ }
+ else
+ {
+ v = uniform!"()"(0.0, 1.0, _rng);
+ }
+
+ while (quot > v)
+ {
+ ++s;
+ quot *= (top - s) / (_available - s);
+ }
+ }
+
+ return s;
+ }
+
+/*
+Randomly reset the value of _Vprime.
+*/
+ private double newVprime(size_t remaining)
+ {
+ static if (is(UniformRNG == void))
+ {
+ double r = uniform!"()"(0.0, 1.0);
+ }
+ else
+ {
+ double r = uniform!"()"(0.0, 1.0, _rng);
+ }
+
+ return r ^^ (1.0 / remaining);
+ }
+
+/*
+Vitter's Algorithm D. For an extensive description of the algorithm
+and its rationale, see:
+
+ * Vitter, J.S. (1984), "Faster methods for random sampling",
+ Commun. ACM 27(7): 703--718
+
+ * Vitter, J.S. (1987) "An efficient algorithm for sequential random
+ sampling", ACM Trans. Math. Softw. 13(1): 58-67.
+
+Variable names are chosen to match those in Vitter's paper.
+*/
+ private size_t skipD()
+ {
+ import std.math : isNaN, trunc;
+ // Confirm that the check in Step D1 is valid and we
+ // haven't been sent here by mistake
+ assert((_alphaInverse * _toSelect) <= _available);
+
+ // Now it's safe to use the standard Algorithm D mechanism.
+ if (_toSelect > 1)
+ {
+ size_t s;
+ size_t qu1 = 1 + _available - _toSelect;
+ double x, y1;
+
+ assert(!_Vprime.isNaN());
+
+ while (true)
+ {
+ // Step D2: set values of x and u.
+ while (1)
+ {
+ x = _available * (1-_Vprime);
+ s = cast(size_t) trunc(x);
+ if (s < qu1)
+ break;
+ _Vprime = newVprime(_toSelect);
+ }
+
+ static if (is(UniformRNG == void))
+ {
+ double u = uniform!"()"(0.0, 1.0);
+ }
+ else
+ {
+ double u = uniform!"()"(0.0, 1.0, _rng);
+ }
+
+ y1 = (u * (cast(double) _available) / qu1) ^^ (1.0/(_toSelect - 1));
+
+ _Vprime = y1 * ((-x/_available)+1.0) * ( qu1/( (cast(double) qu1) - s ) );
+
+ // Step D3: if _Vprime <= 1.0 our work is done and we return S.
+ // Otherwise ...
+ if (_Vprime > 1.0)
+ {
+ size_t top = _available - 1, limit;
+ double y2 = 1.0, bottom;
+
+ if (_toSelect > (s+1))
+ {
+ bottom = _available - _toSelect;
+ limit = _available - s;
+ }
+ else
+ {
+ bottom = _available - (s+1);
+ limit = qu1;
+ }
+
+ foreach (size_t t; limit .. _available)
+ {
+ y2 *= top/bottom;
+ top--;
+ bottom--;
+ }
+
+ // Step D4: decide whether or not to accept the current value of S.
+ if (_available/(_available-x) < y1 * (y2 ^^ (1.0/(_toSelect-1))))
+ {
+ // If it's not acceptable, we generate a new value of _Vprime
+ // and go back to the start of the for (;;) loop.
+ _Vprime = newVprime(_toSelect);
+ }
+ else
+ {
+ // If it's acceptable we generate a new value of _Vprime
+ // based on the remaining number of sample points needed,
+ // and return S.
+ _Vprime = newVprime(_toSelect-1);
+ return s;
+ }
+ }
+ else
+ {
+ // Return if condition D3 satisfied.
+ return s;
+ }
+ }
+ }
+ else
+ {
+ // If only one sample point remains to be taken ...
+ return cast(size_t) trunc(_available * _Vprime);
+ }
+ }
+
+ private void prime()
+ {
+ if (empty)
+ {
+ return;
+ }
+ assert(_available && _available >= _toSelect);
+ immutable size_t s = skip();
+ assert(s + _toSelect <= _available);
+ static if (hasLength!Range)
+ {
+ assert(s + _toSelect <= _input.length);
+ }
+ assert(!_input.empty);
+ _input.popFrontExactly(s);
+ _index += s;
+ _available -= s;
+ assert(_available > 0);
+ }
+}
+
+/// Ditto
+auto randomSample(Range)(Range r, size_t n, size_t total)
+if (isInputRange!Range)
+{
+ return RandomSample!(Range, void)(r, n, total);
+}
+
+/// Ditto
+auto randomSample(Range)(Range r, size_t n)
+if (isInputRange!Range && hasLength!Range)
+{
+ return RandomSample!(Range, void)(r, n, r.length);
+}
+
+/// Ditto
+auto randomSample(Range, UniformRNG)(Range r, size_t n, size_t total, auto ref UniformRNG rng)
+if (isInputRange!Range && isUniformRNG!UniformRNG)
+{
+ return RandomSample!(Range, UniformRNG)(r, n, total, rng);
+}
+
+/// Ditto
+auto randomSample(Range, UniformRNG)(Range r, size_t n, auto ref UniformRNG rng)
+if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG)
+{
+ return RandomSample!(Range, UniformRNG)(r, n, r.length, rng);
+}
+
+@system unittest
+{
+ // @system because it takes the address of a local
+ import std.conv : text;
+ import std.exception;
+ import std.range;
+ // For test purposes, an infinite input range
+ struct TestInputRange
+ {
+ private auto r = recurrence!"a[n-1] + 1"(0);
+ bool empty() @property const pure nothrow { return r.empty; }
+ auto front() @property pure nothrow { return r.front; }
+ void popFront() pure nothrow { r.popFront(); }
+ }
+ static assert(isInputRange!TestInputRange);
+ static assert(!isForwardRange!TestInputRange);
+
+ int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
+
+ foreach (UniformRNG; PseudoRngTypes)
+ {
+ auto rng = UniformRNG(1234);
+ /* First test the most general case: randomSample of input range, with and
+ * without a specified random number generator.
+ */
+ static assert(isInputRange!(typeof(randomSample(TestInputRange(), 5, 10))));
+ static assert(isInputRange!(typeof(randomSample(TestInputRange(), 5, 10, rng))));
+ static assert(!isForwardRange!(typeof(randomSample(TestInputRange(), 5, 10))));
+ static assert(!isForwardRange!(typeof(randomSample(TestInputRange(), 5, 10, rng))));
+ // test case with range initialized by direct call to struct
+ {
+ auto sample =
+ RandomSample!(TestInputRange, UniformRNG)
+ (TestInputRange(), 5, 10, UniformRNG(unpredictableSeed));
+ static assert(isInputRange!(typeof(sample)));
+ static assert(!isForwardRange!(typeof(sample)));
+ }
+
+ /* Now test the case of an input range with length. We ignore the cases
+ * already covered by the previous tests.
+ */
+ static assert(isInputRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5))));
+ static assert(isInputRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5, rng))));
+ static assert(!isForwardRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5))));
+ static assert(!isForwardRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5, rng))));
+ // test case with range initialized by direct call to struct
+ {
+ auto sample =
+ RandomSample!(typeof(TestInputRange().takeExactly(10)), UniformRNG)
+ (TestInputRange().takeExactly(10), 5, 10, UniformRNG(unpredictableSeed));
+ static assert(isInputRange!(typeof(sample)));
+ static assert(!isForwardRange!(typeof(sample)));
+ }
+
+ // Now test the case of providing a forward range as input.
+ static assert(!isForwardRange!(typeof(randomSample(a, 5))));
+ static if (isForwardRange!UniformRNG)
+ {
+ static assert(isForwardRange!(typeof(randomSample(a, 5, rng))));
+ // ... and test with range initialized directly
+ {
+ auto sample =
+ RandomSample!(int[], UniformRNG)
+ (a, 5, UniformRNG(unpredictableSeed));
+ static assert(isForwardRange!(typeof(sample)));
+ }
+ }
+ else
+ {
+ static assert(isInputRange!(typeof(randomSample(a, 5, rng))));
+ static assert(!isForwardRange!(typeof(randomSample(a, 5, rng))));
+ // ... and test with range initialized directly
+ {
+ auto sample =
+ RandomSample!(int[], UniformRNG)
+ (a, 5, UniformRNG(unpredictableSeed));
+ static assert(isInputRange!(typeof(sample)));
+ static assert(!isForwardRange!(typeof(sample)));
+ }
+ }
+
+ /* Check that randomSample will throw an error if we claim more
+ * items are available than there actually are, or if we try to
+ * sample more items than are available. */
+ assert(collectExceptionMsg(
+ randomSample(a, 5, 15)
+ ) == "RandomSample: specified 15 items as available when input contains only 10");
+ assert(collectExceptionMsg(
+ randomSample(a, 15)
+ ) == "RandomSample: cannot sample 15 items when only 10 are available");
+ assert(collectExceptionMsg(
+ randomSample(a, 9, 8)
+ ) == "RandomSample: cannot sample 9 items when only 8 are available");
+ assert(collectExceptionMsg(
+ randomSample(TestInputRange(), 12, 11)
+ ) == "RandomSample: cannot sample 12 items when only 11 are available");
+
+ /* Check that sampling algorithm never accidentally overruns the end of
+ * the input range. If input is an InputRange without .length, this
+ * relies on the user specifying the total number of available items
+ * correctly.
+ */
+ {
+ uint i = 0;
+ foreach (e; randomSample(a, a.length))
+ {
+ assert(e == i);
+ ++i;
+ }
+ assert(i == a.length);
+
+ i = 0;
+ foreach (e; randomSample(TestInputRange(), 17, 17))
+ {
+ assert(e == i);
+ ++i;
+ }
+ assert(i == 17);
+ }
+
+
+ // Check length properties of random samples.
+ assert(randomSample(a, 5).length == 5);
+ assert(randomSample(a, 5, 10).length == 5);
+ assert(randomSample(a, 5, rng).length == 5);
+ assert(randomSample(a, 5, 10, rng).length == 5);
+ assert(randomSample(TestInputRange(), 5, 10).length == 5);
+ assert(randomSample(TestInputRange(), 5, 10, rng).length == 5);
+
+ // ... and emptiness!
+ assert(randomSample(a, 0).empty);
+ assert(randomSample(a, 0, 5).empty);
+ assert(randomSample(a, 0, rng).empty);
+ assert(randomSample(a, 0, 5, rng).empty);
+ assert(randomSample(TestInputRange(), 0, 10).empty);
+ assert(randomSample(TestInputRange(), 0, 10, rng).empty);
+
+ /* Test that the (lazy) evaluation of random samples works correctly.
+ *
+ * We cover 2 different cases: a sample where the ratio of sample points
+ * to total points is greater than the threshold for using Algorithm, and
+ * one where the ratio is small enough (< 1/13) for Algorithm D to be used.
+ *
+ * For each, we also cover the case with and without a specified RNG.
+ */
+ {
+ // Small sample/source ratio, no specified RNG.
+ uint i = 0;
+ foreach (e; randomSample(randomCover(a), 5))
+ {
+ ++i;
+ }
+ assert(i == 5);
+
+ // Small sample/source ratio, specified RNG.
+ i = 0;
+ foreach (e; randomSample(randomCover(a), 5, rng))
+ {
+ ++i;
+ }
+ assert(i == 5);
+
+ // Large sample/source ratio, no specified RNG.
+ i = 0;
+ foreach (e; randomSample(TestInputRange(), 123, 123_456))
+ {
+ ++i;
+ }
+ assert(i == 123);
+
+ // Large sample/source ratio, specified RNG.
+ i = 0;
+ foreach (e; randomSample(TestInputRange(), 123, 123_456, rng))
+ {
+ ++i;
+ }
+ assert(i == 123);
+
+ /* Sample/source ratio large enough to start with Algorithm D,
+ * small enough to switch to Algorithm A.
+ */
+ i = 0;
+ foreach (e; randomSample(TestInputRange(), 10, 131))
+ {
+ ++i;
+ }
+ assert(i == 10);
+ }
+
+ // Test that the .index property works correctly
+ {
+ auto sample1 = randomSample(TestInputRange(), 654, 654_321);
+ for (; !sample1.empty; sample1.popFront())
+ {
+ assert(sample1.front == sample1.index);
+ }
+
+ auto sample2 = randomSample(TestInputRange(), 654, 654_321, rng);
+ for (; !sample2.empty; sample2.popFront())
+ {
+ assert(sample2.front == sample2.index);
+ }
+
+ /* Check that it also works if .index is called before .front.
+ * See: http://d.puremagic.com/issues/show_bug.cgi?id=10322
+ */
+ auto sample3 = randomSample(TestInputRange(), 654, 654_321);
+ for (; !sample3.empty; sample3.popFront())
+ {
+ assert(sample3.index == sample3.front);
+ }
+
+ auto sample4 = randomSample(TestInputRange(), 654, 654_321, rng);
+ for (; !sample4.empty; sample4.popFront())
+ {
+ assert(sample4.index == sample4.front);
+ }
+ }
+
+ /* Test behaviour if .popFront() is called before sample is read.
+ * This is a rough-and-ready check that the statistical properties
+ * are in the ballpark -- not a proper validation of statistical
+ * quality! This incidentally also checks for reference-type
+ * initialization bugs, as the foreach () loop will operate on a
+ * copy of the popFronted (and hence initialized) sample.
+ */
+ {
+ size_t count0, count1, count99;
+ foreach (_; 0 .. 100_000)
+ {
+ auto sample = randomSample(iota(100), 5, &rng);
+ sample.popFront();
+ foreach (s; sample)
+ {
+ if (s == 0)
+ {
+ ++count0;
+ }
+ else if (s == 1)
+ {
+ ++count1;
+ }
+ else if (s == 99)
+ {
+ ++count99;
+ }
+ }
+ }
+ /* Statistical assumptions here: this is a sequential sampling process
+ * so (i) 0 can only be the first sample point, so _can't_ be in the
+ * remainder of the sample after .popFront() is called. (ii) By similar
+ * token, 1 can only be in the remainder if it's the 2nd point of the
+ * whole sample, and hence if 0 was the first; probability of 0 being
+ * first and 1 second is 5/100 * 4/99 (thank you, Algorithm S:-) and
+ * so the mean count of 1 should be about 202. Finally, 99 can only
+ * be the _last_ sample point to be picked, so its probability of
+ * inclusion should be independent of the .popFront() and it should
+ * occur with frequency 5/100, hence its count should be about 5000.
+ * Unfortunately we have to set quite a high tolerance because with
+ * sample size small enough for unittests to run in reasonable time,
+ * the variance can be quite high.
+ */
+ assert(count0 == 0);
+ assert(count1 < 300, text("1: ", count1, " > 300."));
+ assert(4_700 < count99, text("99: ", count99, " < 4700."));
+ assert(count99 < 5_300, text("99: ", count99, " > 5300."));
+ }
+
+ /* Odd corner-cases: RandomSample has 2 constructors that are not called
+ * by the randomSample() helper functions, but that can be used if the
+ * constructor is called directly. These cover the case of the user
+ * specifying input but not input length.
+ */
+ {
+ auto input1 = TestInputRange().takeExactly(456_789);
+ static assert(hasLength!(typeof(input1)));
+ auto sample1 = RandomSample!(typeof(input1), void)(input1, 789);
+ static assert(isInputRange!(typeof(sample1)));
+ static assert(!isForwardRange!(typeof(sample1)));
+ assert(sample1.length == 789);
+ assert(sample1._available == 456_789);
+ uint i = 0;
+ for (; !sample1.empty; sample1.popFront())
+ {
+ assert(sample1.front == sample1.index);
+ ++i;
+ }
+ assert(i == 789);
+
+ auto input2 = TestInputRange().takeExactly(456_789);
+ static assert(hasLength!(typeof(input2)));
+ auto sample2 = RandomSample!(typeof(input2), typeof(rng))(input2, 789, rng);
+ static assert(isInputRange!(typeof(sample2)));
+ static assert(!isForwardRange!(typeof(sample2)));
+ assert(sample2.length == 789);
+ assert(sample2._available == 456_789);
+ i = 0;
+ for (; !sample2.empty; sample2.popFront())
+ {
+ assert(sample2.front == sample2.index);
+ ++i;
+ }
+ assert(i == 789);
+ }
+
+ /* Test that the save property works where input is a forward range,
+ * and RandomSample is using a (forward range) random number generator
+ * that is not rndGen.
+ */
+ static if (isForwardRange!UniformRNG)
+ {
+ auto sample1 = randomSample(a, 5, rng);
+ auto sample2 = sample1.save;
+ assert(sample1.array() == sample2.array());
+ }
+
+ // Bugzilla 8314
+ {
+ auto sample(RandomGen)(uint seed) { return randomSample(a, 1, RandomGen(seed)).front; }
+
+ // Start from 1 because not all RNGs accept 0 as seed.
+ immutable fst = sample!UniformRNG(1);
+ uint n = 1;
+ while (sample!UniformRNG(++n) == fst && n < n.max) {}
+ assert(n < n.max);
+ }
+ }
+}