mirror of
https://github.com/dlang/phobos.git
synced 2025-04-26 13:10:35 +03:00

* Add changelog entry for the `getrandom()` backwards compatibility shim * Comment message pragma of the `getrandom()` backwards compatibility shim
4495 lines
138 KiB
D
4495 lines
138 KiB
D
// Written in the D programming language.
|
||
|
||
/**
|
||
Facilities for random number generation.
|
||
|
||
$(SCRIPT inhibitQuickIndex = 1;)
|
||
$(DIVC quickindex,
|
||
$(BOOKTABLE,
|
||
$(TR $(TH Category) $(TH Functions))
|
||
$(TR $(TD Uniform sampling) $(TD
|
||
$(LREF uniform)
|
||
$(LREF uniform01)
|
||
$(LREF uniformDistribution)
|
||
))
|
||
$(TR $(TD Element sampling) $(TD
|
||
$(LREF choice)
|
||
$(LREF dice)
|
||
))
|
||
$(TR $(TD Range sampling) $(TD
|
||
$(LREF randomCover)
|
||
$(LREF randomSample)
|
||
))
|
||
$(TR $(TD Default Random Engines) $(TD
|
||
$(LREF rndGen)
|
||
$(LREF Random)
|
||
$(LREF unpredictableSeed)
|
||
))
|
||
$(TR $(TD Linear Congruential Engines) $(TD
|
||
$(LREF MinstdRand)
|
||
$(LREF MinstdRand0)
|
||
$(LREF LinearCongruentialEngine)
|
||
))
|
||
$(TR $(TD Mersenne Twister Engines) $(TD
|
||
$(LREF Mt19937)
|
||
$(LREF Mt19937_64)
|
||
$(LREF MersenneTwisterEngine)
|
||
))
|
||
$(TR $(TD Xorshift Engines) $(TD
|
||
$(LREF Xorshift)
|
||
$(LREF XorshiftEngine)
|
||
$(LREF Xorshift32)
|
||
$(LREF Xorshift64)
|
||
$(LREF Xorshift96)
|
||
$(LREF Xorshift128)
|
||
$(LREF Xorshift160)
|
||
$(LREF Xorshift192)
|
||
))
|
||
$(TR $(TD Shuffle) $(TD
|
||
$(LREF partialShuffle)
|
||
$(LREF randomShuffle)
|
||
))
|
||
$(TR $(TD Traits) $(TD
|
||
$(LREF isSeedable)
|
||
$(LREF isUniformRNG)
|
||
))
|
||
))
|
||
|
||
$(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 `MinstdRand0` and `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
|
||
{
|
||
import std.algorithm.comparison : among, equal;
|
||
import std.range : iota;
|
||
|
||
// 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 >= 0 && i < 15);
|
||
|
||
// Generate a uniformly-distributed real in the range [0, 100)
|
||
auto r = uniform(0.0L, 100.0L, rnd);
|
||
assert(r >= 0 && r < 100);
|
||
|
||
// Sample from a custom type
|
||
enum Fruit { apple, mango, pear }
|
||
auto f = rnd.uniform!Fruit;
|
||
with(Fruit)
|
||
assert(f.among(apple, mango, pear));
|
||
|
||
// Generate a 32-bit random number
|
||
auto u = uniform!uint(rnd);
|
||
static assert(is(typeof(u) == uint));
|
||
|
||
// Generate a random number in the range in the range [0, 1)
|
||
auto u2 = uniform01(rnd);
|
||
assert(u2 >= 0 && u2 < 1);
|
||
|
||
// Select an element randomly
|
||
auto el = 10.iota.choice(rnd);
|
||
assert(0 <= el && el < 10);
|
||
|
||
// Throw a dice with custom proportions
|
||
// 0: 20%, 1: 10%, 2: 60%
|
||
auto val = rnd.dice(0.2, 0.1, 0.6);
|
||
assert(0 <= val && val <= 2);
|
||
|
||
auto rnd2 = MinstdRand0(42);
|
||
|
||
// Select a random subsample from a range
|
||
assert(10.iota.randomSample(3, rnd2).equal([7, 8, 9]));
|
||
|
||
// Cover all elements in an array in random order
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert(10.iota.randomCover(rnd2).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5]));
|
||
else
|
||
assert(10.iota.randomCover(rnd2).equal([4, 8, 7, 3, 5, 9, 2, 6, 0, 1]));
|
||
|
||
// Shuffle an array
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert([0, 1, 2, 4, 5].randomShuffle(rnd2).equal([2, 0, 4, 5, 1]));
|
||
else
|
||
assert([0, 1, 2, 4, 5].randomShuffle(rnd2).equal([4, 2, 5, 0, 1]));
|
||
}
|
||
|
||
version (OSX)
|
||
version = Darwin;
|
||
else version (iOS)
|
||
version = Darwin;
|
||
else version (TVOS)
|
||
version = Darwin;
|
||
else version (WatchOS)
|
||
version = Darwin;
|
||
|
||
version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
|
||
version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
|
||
|
||
version (StdUnittest)
|
||
{
|
||
static import std.meta;
|
||
package alias Xorshift64_64 = XorshiftEngine!(ulong, 64, -12, 25, -27);
|
||
package alias Xorshift128_64 = XorshiftEngine!(ulong, 128, 23, -18, -5);
|
||
package alias PseudoRngTypes = std.meta.AliasSeq!(MinstdRand0, MinstdRand, Mt19937, Xorshift32, Xorshift64,
|
||
Xorshift96, Xorshift128, Xorshift160, Xorshift192,
|
||
Xorshift64_64, Xorshift128_64);
|
||
}
|
||
|
||
// 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 = .isUniformRNG!Rng &&
|
||
is(std.range.primitives.ElementType!Rng == ElementType);
|
||
}
|
||
|
||
/**
|
||
* ditto
|
||
*/
|
||
template isUniformRNG(Rng)
|
||
{
|
||
enum bool isUniformRNG = isInputRange!Rng &&
|
||
is(typeof(
|
||
{
|
||
static assert(Rng.isUniformRandom); //tag
|
||
}));
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
struct NoRng
|
||
{
|
||
@property uint front() {return 0;}
|
||
@property bool empty() {return false;}
|
||
void popFront() {}
|
||
}
|
||
static assert(!isUniformRNG!(NoRng));
|
||
|
||
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));
|
||
}
|
||
|
||
@safe unittest
|
||
{
|
||
// two-argument predicate should not require @property on `front`
|
||
// https://issues.dlang.org/show_bug.cgi?id=19837
|
||
struct Rng
|
||
{
|
||
float front() {return 0;}
|
||
void popFront() {}
|
||
enum empty = false;
|
||
enum isUniformRandom = true;
|
||
}
|
||
static assert(isUniformRNG!(Rng, float));
|
||
}
|
||
|
||
/**
|
||
* 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
|
||
SeedType s = void;
|
||
r.seed(s); // can seed a Rng
|
||
}));
|
||
}
|
||
|
||
///ditto
|
||
template isSeedable(Rng)
|
||
{
|
||
enum bool isSeedable = isUniformRNG!Rng &&
|
||
is(typeof(
|
||
{
|
||
Rng r = void; // can define a Rng object
|
||
alias SeedType = typeof(r.front);
|
||
SeedType s = void;
|
||
r.seed(s); // can seed a Rng
|
||
}));
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
struct validRng
|
||
{
|
||
@property uint front() {return 0;}
|
||
@property bool empty() {return false;}
|
||
void popFront() {}
|
||
|
||
enum isUniformRandom = true;
|
||
}
|
||
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(isSeedable!(seedRng, uint));
|
||
static assert(!isSeedable!(seedRng, ulong));
|
||
static assert(isSeedable!(seedRng));
|
||
}
|
||
|
||
@safe @nogc 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, ulong));
|
||
static assert(isSeedable!(seedRng));
|
||
}
|
||
|
||
/**
|
||
Linear Congruential generator. When m = 0, no modulus is used.
|
||
*/
|
||
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 (`1` if $(D c == 0), `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
|
||
`x0`.
|
||
*/
|
||
this(UIntType x0) @safe pure nothrow @nogc
|
||
{
|
||
seed(x0);
|
||
}
|
||
|
||
/**
|
||
(Re)seeds the generator.
|
||
*/
|
||
void seed(UIntType x0 = 1) @safe pure nothrow @nogc
|
||
{
|
||
_x = modulus ? (x0 % modulus) : x0;
|
||
static if (c == 0)
|
||
{
|
||
//Necessary to prevent generator from outputting an endless series of zeroes.
|
||
if (_x == 0)
|
||
_x = max;
|
||
}
|
||
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() const @safe pure nothrow @nogc
|
||
{
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
Always `false` (random generators are infinite ranges).
|
||
*/
|
||
enum bool empty = false;
|
||
|
||
// https://issues.dlang.org/show_bug.cgi?id=21610
|
||
static if (m)
|
||
{
|
||
private UIntType _x = (a + c) % m;
|
||
}
|
||
else
|
||
{
|
||
private UIntType _x = a + c;
|
||
}
|
||
}
|
||
|
||
/// Declare your own linear congruential engine
|
||
@safe unittest
|
||
{
|
||
alias CPP11LCG = LinearCongruentialEngine!(uint, 48271, 0, 2_147_483_647);
|
||
|
||
// seed with a constant
|
||
auto rnd = CPP11LCG(42);
|
||
auto n = rnd.front; // same for each run
|
||
assert(n == 2027382);
|
||
}
|
||
|
||
/// Declare your own linear congruential engine
|
||
@safe unittest
|
||
{
|
||
// glibc's LCG
|
||
alias GLibcLCG = LinearCongruentialEngine!(uint, 1103515245, 12345, 2_147_483_648);
|
||
|
||
// Seed with an unpredictable value
|
||
auto rnd = GLibcLCG(unpredictableSeed);
|
||
auto n = rnd.front; // different across runs
|
||
}
|
||
|
||
/// Declare your own linear congruential engine
|
||
@safe unittest
|
||
{
|
||
// Visual C++'s LCG
|
||
alias MSVCLCG = LinearCongruentialEngine!(uint, 214013, 2531011, 0);
|
||
|
||
// seed with a constant
|
||
auto rnd = MSVCLCG(1);
|
||
auto n = rnd.front; // same for each run
|
||
assert(n == 2745024);
|
||
}
|
||
|
||
// Ensure that unseeded LCGs produce correct values
|
||
@safe unittest
|
||
{
|
||
alias LGE = LinearCongruentialEngine!(uint, 10000, 19682, 19683);
|
||
|
||
LGE rnd;
|
||
assert(rnd.front == 9999);
|
||
}
|
||
|
||
/**
|
||
Define $(D_PARAM LinearCongruentialEngine) generators with well-chosen
|
||
parameters. `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. `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 @nogc unittest
|
||
{
|
||
// seed with a constant
|
||
auto rnd0 = MinstdRand0(1);
|
||
auto n = rnd0.front;
|
||
// same for each run
|
||
assert(n == 16807);
|
||
|
||
// Seed with an unpredictable value
|
||
rnd0.seed(unpredictableSeed);
|
||
n = rnd0.front; // different across runs
|
||
}
|
||
|
||
@safe @nogc 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
|
||
enum ulong[20] 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
|
||
enum ulong[6] 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
|
||
static foreach (Type; std.meta.AliasSeq!(MinstdRand0, MinstdRand))
|
||
{{
|
||
auto rnd1 = Type(123_456_789);
|
||
rnd1.popFront();
|
||
// https://issues.dlang.org/show_bug.cgi?id=15853
|
||
auto rnd2 = ((const ref Type a) => a.save())(rnd1);
|
||
assert(rnd1 == rnd2);
|
||
// Enable next test when RNGs are reference types
|
||
version (none) { assert(rnd1 !is rnd2); }
|
||
for (auto i = 0; i < 3; i++, rnd1.popFront, rnd2.popFront)
|
||
assert(rnd1.front() == rnd2.front());
|
||
}}
|
||
}
|
||
|
||
@safe @nogc unittest
|
||
{
|
||
auto rnd0 = MinstdRand0(MinstdRand0.modulus);
|
||
auto n = rnd0.front;
|
||
rnd0.popFront();
|
||
assert(n != rnd0.front);
|
||
}
|
||
|
||
/**
|
||
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 <= ptrdiff_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) & 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) @nogc
|
||
{
|
||
mtState.data[$ - 1] = value;
|
||
static if (max != UIntType.max)
|
||
{
|
||
mtState.data[$ - 1] &= 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 (max != UIntType.max)
|
||
{
|
||
e &= 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:
|
||
`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(immutable ElementType!T == immutable 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(immutable ElementType!T == immutable UIntType))
|
||
{
|
||
size_t j;
|
||
for (j = 0; j < n && !range.empty; ++j, range.popFront())
|
||
{
|
||
ptrdiff_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) ~ " 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) @nogc
|
||
{
|
||
/* 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. */
|
||
ptrdiff_t index = mtState.index;
|
||
ptrdiff_t next = index - 1;
|
||
if (next < 0)
|
||
next = n - 1;
|
||
auto z = mtState.z;
|
||
ptrdiff_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 const pure nothrow @nogc
|
||
{
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
Always `false`.
|
||
*/
|
||
enum bool empty = false;
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
// seed with a constant
|
||
Mt19937 gen;
|
||
auto n = gen.front; // same for each run
|
||
assert(n == 3499211612);
|
||
|
||
// Seed with an unpredictable value
|
||
gen.seed(unpredictableSeed);
|
||
n = gen.front; // different across runs
|
||
}
|
||
|
||
/**
|
||
A `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 $(LREF
|
||
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 @nogc unittest
|
||
{
|
||
// seed with a constant
|
||
Mt19937 gen;
|
||
auto n = gen.front; // same for each run
|
||
assert(n == 3499211612);
|
||
|
||
// 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 `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 @nogc unittest
|
||
{
|
||
// Seed with a constant
|
||
auto gen = Mt19937_64(12345);
|
||
auto n = gen.front; // same for each run
|
||
assert(n == 6597103971274460346);
|
||
|
||
// Seed with an unpredictable value
|
||
gen.seed(unpredictableSeed!ulong);
|
||
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));
|
||
static assert(isSeedable!(Mt19937_64, typeof(map!((a) => unpredictableSeed!ulong)(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) => 123_456_789U)(repeat(0, 623))));
|
||
|
||
gen.seed(123_456_789U.repeat(624));
|
||
//infinite Range
|
||
gen.seed(123_456_789U.repeat);
|
||
}
|
||
|
||
@safe @nogc 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 @nogc unittest
|
||
{
|
||
// Check .save works
|
||
static foreach (Type; std.meta.AliasSeq!(Mt19937, Mt19937_64))
|
||
{{
|
||
auto gen1 = Type(123_456_789);
|
||
gen1.popFront();
|
||
// https://issues.dlang.org/show_bug.cgi?id=15853
|
||
auto gen2 = ((const ref Type a) => a.save())(gen1);
|
||
assert(gen1 == gen2); // Danger, Will Robinson -- no opEquals for MT
|
||
// Enable next test when RNGs are reference types
|
||
version (none) { assert(gen1 !is gen2); }
|
||
for (auto i = 0; i < 100; i++, gen1.popFront, gen2.popFront)
|
||
assert(gen1.front() == gen2.front());
|
||
}}
|
||
}
|
||
|
||
// https://issues.dlang.org/show_bug.cgi?id=11690
|
||
@safe @nogc pure nothrow unittest
|
||
{
|
||
alias MT(UIntType, uint w) = MersenneTwisterEngine!(UIntType, w, 624, 397, 31,
|
||
0x9908b0df, 11, 0xffffffff, 7,
|
||
0x9d2c5680, 15,
|
||
0xefc60000, 18, 1812433253);
|
||
|
||
static immutable ulong[] expectedFirstValue = [3499211612uL, 3499211612uL,
|
||
171143175841277uL, 1145028863177033374uL];
|
||
|
||
static immutable ulong[] expected10kValue = [4123659995uL, 4123659995uL,
|
||
51991688252792uL, 3031481165133029945uL];
|
||
|
||
static 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.
|
||
Implemented according to $(HTTP www.jstatsoft.org/v08/i14/paper, Xorshift RNGs)
|
||
(Marsaglia, 2003) when the size is small. For larger sizes the generator
|
||
uses Sebastino Vigna's optimization of using an index to avoid needing
|
||
to rotate the internal array.
|
||
|
||
Period is `2 ^^ nbits - 1` except for a legacy 192-bit uint version (see
|
||
note below).
|
||
|
||
Params:
|
||
UIntType = Word size of this xorshift generator and the return type
|
||
of `opCall`.
|
||
nbits = The number of bits of state of this generator. This must be
|
||
a positive multiple of the size in bits of UIntType. If
|
||
nbits is large this struct may occupy slightly more memory
|
||
than this so it can use a circular counter instead of
|
||
shifting the entire array.
|
||
sa = The direction and magnitude of the 1st shift. Positive
|
||
means left, negative means right.
|
||
sb = The direction and magnitude of the 2nd shift. Positive
|
||
means left, negative means right.
|
||
sc = The direction and magnitude of the 3rd shift. Positive
|
||
means left, negative means right.
|
||
|
||
Note:
|
||
For historical compatibility when `nbits == 192` and `UIntType` is `uint`
|
||
a legacy hybrid PRNG is used consisting of a 160-bit xorshift combined
|
||
with a 32-bit counter. This combined generator has period equal to the
|
||
least common multiple of `2^^160 - 1` and `2^^32`.
|
||
|
||
Previous versions of `XorshiftEngine` did not provide any mechanism to specify
|
||
the directions of the shifts, taking each shift as an unsigned magnitude.
|
||
For backwards compatibility, because three shifts in the same direction
|
||
cannot result in a full-period XorshiftEngine, when all three of `sa`, `sb`,
|
||
`sc, are positive `XorshiftEngine` treats them as unsigned magnitudes and
|
||
uses shift directions to match the old behavior of `XorshiftEngine`.
|
||
|
||
Not every set of shifts results in a full-period xorshift generator.
|
||
The template does not currently at compile-time perform a full check
|
||
for maximum period but in a future version might reject parameters
|
||
resulting in shorter periods.
|
||
+/
|
||
struct XorshiftEngine(UIntType, uint nbits, int sa, int sb, int sc)
|
||
if (isUnsigned!UIntType && !(sa > 0 && sb > 0 && sc > 0))
|
||
{
|
||
static assert(nbits > 0 && nbits % (UIntType.sizeof * 8) == 0,
|
||
"nbits must be an even multiple of "~UIntType.stringof
|
||
~".sizeof * 8, not "~nbits.stringof~".");
|
||
|
||
static assert(!((sa >= 0) == (sb >= 0) && (sa >= 0) == (sc >= 0))
|
||
&& (sa * sb * sc != 0),
|
||
"shifts cannot be zero and cannot all be in same direction: cannot be ["
|
||
~sa.stringof~", "~sb.stringof~", "~sc.stringof~"].");
|
||
|
||
static assert(sa != sb && sb != sc,
|
||
"consecutive shifts with the same magnitude and direction would partially or completely cancel!");
|
||
|
||
static assert(UIntType.sizeof == uint.sizeof || UIntType.sizeof == ulong.sizeof,
|
||
"XorshiftEngine currently does not support type " ~ UIntType.sizeof
|
||
~ " because it does not have a `seed` implementation for arrays "
|
||
~ " of element types other than uint and ulong.");
|
||
|
||
public:
|
||
///Mark this as a Rng
|
||
enum bool isUniformRandom = true;
|
||
/// Always `false` (random generators are infinite ranges).
|
||
enum empty = false;
|
||
/// Smallest generated value.
|
||
enum UIntType min = _state.length == 1 ? 1 : 0;
|
||
/// Largest generated value.
|
||
enum UIntType max = UIntType.max;
|
||
|
||
|
||
private:
|
||
// Legacy 192 bit uint hybrid counter/xorshift.
|
||
enum bool isLegacy192Bit = UIntType.sizeof == uint.sizeof && nbits == 192;
|
||
|
||
// Shift magnitudes.
|
||
enum a = (sa < 0 ? -sa : sa);
|
||
enum b = (sb < 0 ? -sb : sb);
|
||
enum c = (sc < 0 ? -sc : sc);
|
||
|
||
// Shift expressions to mix in.
|
||
enum shiftA(string expr) = `((`~expr~`) `~(sa > 0 ? `<< a)` : ` >>> a)`);
|
||
enum shiftB(string expr) = `((`~expr~`) `~(sb > 0 ? `<< b)` : ` >>> b)`);
|
||
enum shiftC(string expr) = `((`~expr~`) `~(sc > 0 ? `<< c)` : ` >>> c)`);
|
||
|
||
enum N = nbits / (UIntType.sizeof * 8);
|
||
|
||
// For N <= 2 it is strictly worse to use an index.
|
||
// Informal third-party benchmarks suggest that for `ulong` it is
|
||
// faster to use an index when N == 4. For `uint` we err on the side
|
||
// of not increasing the struct's size and only switch to the other
|
||
// implementation when N > 4.
|
||
enum useIndex = !isLegacy192Bit && (UIntType.sizeof >= ulong.sizeof ? N > 3 : N > 4);
|
||
static if (useIndex)
|
||
{
|
||
enum initialIndex = N - 1;
|
||
uint _index = initialIndex;
|
||
}
|
||
|
||
static if (N == 1 && UIntType.sizeof <= uint.sizeof)
|
||
{
|
||
UIntType[N] _state = [cast(UIntType) 2_463_534_242];
|
||
}
|
||
else static if (isLegacy192Bit)
|
||
{
|
||
UIntType[N] _state = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321, 6_615_241];
|
||
UIntType value_;
|
||
}
|
||
else static if (N <= 5 && UIntType.sizeof <= uint.sizeof)
|
||
{
|
||
UIntType[N] _state = [
|
||
cast(UIntType) 123_456_789,
|
||
cast(UIntType) 362_436_069,
|
||
cast(UIntType) 521_288_629,
|
||
cast(UIntType) 88_675_123,
|
||
cast(UIntType) 5_783_321][0 .. N];
|
||
}
|
||
else
|
||
{
|
||
UIntType[N] _state = ()
|
||
{
|
||
static if (UIntType.sizeof < ulong.sizeof)
|
||
{
|
||
uint x0 = 123_456_789;
|
||
enum uint m = 1_812_433_253U;
|
||
}
|
||
else static if (UIntType.sizeof <= ulong.sizeof)
|
||
{
|
||
ulong x0 = 123_456_789;
|
||
enum ulong m = 6_364_136_223_846_793_005UL;
|
||
}
|
||
else
|
||
{
|
||
static assert(0, "Phobos Error: Xorshift has no instantiation rule for "
|
||
~ UIntType.stringof);
|
||
}
|
||
enum uint rshift = typeof(x0).sizeof * 8 - 2;
|
||
UIntType[N] result = void;
|
||
foreach (i, ref e; result)
|
||
{
|
||
e = cast(UIntType) (x0 = (m * (x0 ^ (x0 >>> rshift)) + i + 1));
|
||
if (e == 0)
|
||
e = cast(UIntType) (i + 1);
|
||
}
|
||
return result;
|
||
}();
|
||
}
|
||
|
||
|
||
public:
|
||
/++
|
||
Constructs a `XorshiftEngine` generator seeded with $(D_PARAM x0).
|
||
|
||
Params:
|
||
x0 = value used to deterministically initialize internal state
|
||
+/
|
||
this()(UIntType x0) @safe pure nothrow @nogc
|
||
{
|
||
seed(x0);
|
||
}
|
||
|
||
|
||
/++
|
||
(Re)seeds the generator.
|
||
|
||
Params:
|
||
x0 = value used to deterministically initialize internal state
|
||
+/
|
||
void seed()(UIntType x0) @safe pure nothrow @nogc
|
||
{
|
||
static if (useIndex)
|
||
_index = initialIndex;
|
||
|
||
static if (UIntType.sizeof == uint.sizeof)
|
||
{
|
||
// Initialization routine from MersenneTwisterEngine.
|
||
foreach (uint i, ref e; _state)
|
||
{
|
||
e = (x0 = (1_812_433_253U * (x0 ^ (x0 >> 30)) + i + 1));
|
||
// Xorshift requires merely that not every word of the internal
|
||
// array is 0. For historical compatibility the 32-bit word version
|
||
// has the stronger requirement that not any word of the state
|
||
// array is 0 after initial seeding.
|
||
if (e == 0)
|
||
e = (i + 1);
|
||
}
|
||
}
|
||
else static if (UIntType.sizeof == ulong.sizeof)
|
||
{
|
||
static if (N > 1)
|
||
{
|
||
// Initialize array using splitmix64 as recommended by Sebastino Vigna.
|
||
// By construction the array will not be all zeroes.
|
||
// http://xoroshiro.di.unimi.it/splitmix64.c
|
||
foreach (ref e; _state)
|
||
{
|
||
ulong z = (x0 += 0x9e37_79b9_7f4a_7c15UL);
|
||
z = (z ^ (z >>> 30)) * 0xbf58_476d_1ce4_e5b9UL;
|
||
z = (z ^ (z >>> 27)) * 0x94d0_49bb_1331_11ebUL;
|
||
e = z ^ (z >>> 31);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Apply a transformation when N == 1 instead of just copying x0
|
||
// directly because it's not unlikely that a user might initialize
|
||
// a PRNG with small counting numbers (e.g. 1, 2, 3) that have the
|
||
// statistically rare property of having only 1 or 2 non-zero bits.
|
||
// Additionally we need to ensure that the internal state is not
|
||
// entirely zero.
|
||
if (x0 != 0)
|
||
_state[0] = x0 * 6_364_136_223_846_793_005UL;
|
||
else
|
||
_state[0] = typeof(this).init._state[0];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
static assert(0, "Phobos Error: Xorshift has no `seed` implementation for "
|
||
~ UIntType.stringof);
|
||
}
|
||
|
||
popFront();
|
||
}
|
||
|
||
|
||
/**
|
||
* Returns the current number in the random sequence.
|
||
*/
|
||
@property
|
||
UIntType front() const @safe pure nothrow @nogc
|
||
{
|
||
static if (isLegacy192Bit)
|
||
return value_;
|
||
else static if (!useIndex)
|
||
return _state[N-1];
|
||
else
|
||
return _state[_index];
|
||
}
|
||
|
||
|
||
/**
|
||
* Advances the random sequence.
|
||
*/
|
||
void popFront() @safe pure nothrow @nogc
|
||
{
|
||
alias s = _state;
|
||
static if (isLegacy192Bit)
|
||
{
|
||
auto x = _state[0] ^ mixin(shiftA!`s[0]`);
|
||
static foreach (i; 0 .. N-2)
|
||
s[i] = s[i + 1];
|
||
s[N-2] = s[N-2] ^ mixin(shiftC!`s[N-2]`) ^ x ^ mixin(shiftB!`x`);
|
||
value_ = s[N-2] + (s[N-1] += 362_437);
|
||
}
|
||
else static if (N == 1)
|
||
{
|
||
s[0] ^= mixin(shiftA!`s[0]`);
|
||
s[0] ^= mixin(shiftB!`s[0]`);
|
||
s[0] ^= mixin(shiftC!`s[0]`);
|
||
}
|
||
else static if (!useIndex)
|
||
{
|
||
auto x = s[0] ^ mixin(shiftA!`s[0]`);
|
||
static foreach (i; 0 .. N-1)
|
||
s[i] = s[i + 1];
|
||
s[N-1] = s[N-1] ^ mixin(shiftC!`s[N-1]`) ^ x ^ mixin(shiftB!`x`);
|
||
}
|
||
else
|
||
{
|
||
assert(_index < N); // Invariant.
|
||
const sIndexMinus1 = s[_index];
|
||
static if ((N & (N - 1)) == 0)
|
||
_index = (_index + 1) & typeof(_index)(N - 1);
|
||
else
|
||
{
|
||
if (++_index >= N)
|
||
_index = 0;
|
||
}
|
||
auto x = s[_index];
|
||
x ^= mixin(shiftA!`x`);
|
||
s[_index] = sIndexMinus1 ^ mixin(shiftC!`sIndexMinus1`) ^ x ^ mixin(shiftB!`x`);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Captures a range state.
|
||
*/
|
||
@property
|
||
typeof(this) save() const @safe pure nothrow @nogc
|
||
{
|
||
return this;
|
||
}
|
||
|
||
private:
|
||
// Workaround for a DScanner bug. If we remove this `private:` DScanner
|
||
// gives erroneous warnings about missing documentation for public symbols
|
||
// later in the module.
|
||
}
|
||
|
||
/// ditto
|
||
template XorshiftEngine(UIntType, int bits, int a, int b, int c)
|
||
if (isUnsigned!UIntType && a > 0 && b > 0 && c > 0)
|
||
{
|
||
// Compatibility with old parameterizations without explicit shift directions.
|
||
static if (bits == UIntType.sizeof * 8)
|
||
alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, a, -b, c);//left, right, left
|
||
else static if (bits == 192 && UIntType.sizeof == uint.sizeof)
|
||
alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, -a, b, c);//right, left, left
|
||
else
|
||
alias XorshiftEngine = .XorshiftEngine!(UIntType, bits, a, -b, -c);//left, right, right
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
alias Xorshift96 = XorshiftEngine!(uint, 96, 10, 5, 26);
|
||
auto rnd = Xorshift96(42);
|
||
auto num = rnd.front; // same for each run
|
||
assert(num == 2704588748);
|
||
}
|
||
|
||
|
||
/**
|
||
* Define `XorshiftEngine` generators with well-chosen parameters. See each bits examples of "Xorshift RNGs".
|
||
* `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 @nogc unittest
|
||
{
|
||
// Seed with a constant
|
||
auto rnd = Xorshift(1);
|
||
auto num = rnd.front; // same for each run
|
||
assert(num == 1405313047);
|
||
|
||
// Seed with an unpredictable value
|
||
rnd.seed(unpredictableSeed);
|
||
num = rnd.front; // different across rnd
|
||
}
|
||
|
||
@safe @nogc 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));
|
||
|
||
static assert(Xorshift32.min == 1);
|
||
|
||
// Result from reference implementation.
|
||
static ulong[][] 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],
|
||
[16749904790159980466UL, 14489774923612894650UL, 148813570191443371UL,
|
||
6529783008780612886UL, 10182425759614080046UL, 16549659571055687844UL,
|
||
542957868271744939UL, 9459127085596028450UL, 16001049981702441780UL,
|
||
7351634712593111741],
|
||
[14750058843113249055UL, 17731577314455387619UL, 1314705253499959044UL,
|
||
3113030620614841056UL, 9468075444678629182UL, 13962152036600088141UL,
|
||
9030205609946043947UL, 1856726150434672917UL, 8098922200110395314UL,
|
||
2772699174618556175UL],
|
||
];
|
||
|
||
alias XorshiftTypes = std.meta.AliasSeq!(Xorshift32, Xorshift64, Xorshift96,
|
||
Xorshift128, Xorshift160, Xorshift192, Xorshift64_64, Xorshift128_64);
|
||
|
||
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(123_456_789);
|
||
rnd1.popFront();
|
||
// https://issues.dlang.org/show_bug.cgi?id=15853
|
||
auto rnd2 = ((const ref Type a) => a.save())(rnd1);
|
||
assert(rnd1 == rnd2);
|
||
// Enable next test when RNGs are reference types
|
||
version (none) { assert(rnd1 !is rnd2); }
|
||
for (auto i = 0; i <= Type.sizeof / 4; i++, rnd1.popFront, rnd2.popFront)
|
||
assert(rnd1.front() == rnd2.front());
|
||
}
|
||
}
|
||
|
||
|
||
/* 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 @nogc unittest
|
||
{
|
||
foreach (Rng; PseudoRngTypes)
|
||
{
|
||
static assert(isUniformRNG!Rng);
|
||
auto rng = Rng(123_456_789);
|
||
}
|
||
}
|
||
|
||
version (CRuntime_Bionic)
|
||
version = SecureARC4Random; // ChaCha20
|
||
version (Darwin)
|
||
version = SecureARC4Random; // AES
|
||
version (OpenBSD)
|
||
version = SecureARC4Random; // ChaCha20
|
||
version (NetBSD)
|
||
version = SecureARC4Random; // ChaCha20
|
||
|
||
version (CRuntime_UClibc)
|
||
version = LegacyARC4Random; // ARC4
|
||
version (FreeBSD)
|
||
version = LegacyARC4Random; // ARC4
|
||
version (DragonFlyBSD)
|
||
version = LegacyARC4Random; // ARC4
|
||
version (BSD)
|
||
version = LegacyARC4Random; // Unknown implementation
|
||
|
||
// For the current purpose of unpredictableSeed the difference between
|
||
// a secure arc4random implementation and a legacy implementation is
|
||
// unimportant. The source code documents this distinction in case in the
|
||
// future Phobos is altered to require cryptographically secure sources
|
||
// of randomness, and also so other people reading this source code (as
|
||
// Phobos is often looked to as an example of good D programming practices)
|
||
// do not mistakenly use insecure versions of arc4random in contexts where
|
||
// cryptographically secure sources of randomness are needed.
|
||
|
||
// Performance note: ChaCha20 is about 70% faster than ARC4, contrary to
|
||
// what one might assume from it being more secure.
|
||
|
||
version (SecureARC4Random)
|
||
version = AnyARC4Random;
|
||
version (LegacyARC4Random)
|
||
version = AnyARC4Random;
|
||
|
||
version (AnyARC4Random)
|
||
{
|
||
extern(C) private @nogc nothrow
|
||
{
|
||
uint arc4random() @safe;
|
||
void arc4random_buf(scope void* buf, size_t nbytes) @system;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
private ulong bootstrapSeed() @nogc nothrow
|
||
{
|
||
// https://issues.dlang.org/show_bug.cgi?id=19580
|
||
// previously used `ulong result = void` to start with an arbitary value
|
||
// but using an uninitialized variable's value is undefined behavior
|
||
// and enabled unwanted optimizations on non-DMD compilers.
|
||
ulong result;
|
||
enum ulong m = 0xc6a4_a793_5bd1_e995UL; // MurmurHash2_64A constant.
|
||
void updateResult(ulong x)
|
||
{
|
||
x *= m;
|
||
x = (x ^ (x >>> 47)) * m;
|
||
result = (result ^ x) * m;
|
||
}
|
||
import core.thread : getpid, Thread;
|
||
import core.time : MonoTime;
|
||
|
||
updateResult(cast(ulong) cast(void*) Thread.getThis());
|
||
updateResult(cast(ulong) getpid());
|
||
updateResult(cast(ulong) MonoTime.currTime.ticks);
|
||
result = (result ^ (result >>> 47)) * m;
|
||
return result ^ (result >>> 47);
|
||
}
|
||
|
||
// If we don't have arc4random and we don't have RDRAND fall back to this.
|
||
private ulong fallbackSeed() @nogc nothrow
|
||
{
|
||
// Bit avalanche function from MurmurHash3.
|
||
static ulong fmix64(ulong k) @nogc nothrow pure @safe
|
||
{
|
||
k = (k ^ (k >>> 33)) * 0xff51afd7ed558ccd;
|
||
k = (k ^ (k >>> 33)) * 0xc4ceb9fe1a85ec53;
|
||
return k ^ (k >>> 33);
|
||
}
|
||
// Using SplitMix algorithm with constant gamma.
|
||
// Chosen gamma is the odd number closest to 2^^64
|
||
// divided by the silver ratio (1.0L + sqrt(2.0L)).
|
||
enum gamma = 0x6a09e667f3bcc909UL;
|
||
import core.atomic : has64BitCAS;
|
||
static if (has64BitCAS)
|
||
{
|
||
import core.atomic : MemoryOrder, atomicLoad, atomicOp, atomicStore, cas;
|
||
shared static ulong seed;
|
||
shared static bool initialized;
|
||
if (0 == atomicLoad!(MemoryOrder.raw)(initialized))
|
||
{
|
||
cas(&seed, 0UL, fmix64(bootstrapSeed()));
|
||
atomicStore!(MemoryOrder.rel)(initialized, true);
|
||
}
|
||
return fmix64(atomicOp!"+="(seed, gamma));
|
||
}
|
||
else
|
||
{
|
||
static ulong seed;
|
||
static bool initialized;
|
||
if (!initialized)
|
||
{
|
||
seed = fmix64(bootstrapSeed());
|
||
initialized = true;
|
||
}
|
||
return fmix64(seed += gamma);
|
||
}
|
||
}
|
||
}
|
||
|
||
version (linux)
|
||
{
|
||
version (linux_legacy_emulate_getrandom)
|
||
{
|
||
/+
|
||
Emulates `getrandom()` for backwards compatibility
|
||
with outdated kernels and legacy libc versions.
|
||
|
||
`getrandom()` was added to the GNU C Library in v2.25.
|
||
+/
|
||
|
||
/+
|
||
On modern kernels (5.6+), `/dev/random` would behave more similar
|
||
to `getrandom()`.
|
||
However, this emulator was specifically written for systems older
|
||
than that. Hence, `/dev/urandom` is the CSPRNG of choice.
|
||
|
||
<https://web.archive.org/web/20200914181930/https://www.2uo.de/myths-about-urandom/>
|
||
+/
|
||
private static immutable _pathLinuxSystemCSPRNG = "/dev/urandom";
|
||
|
||
import core.sys.posix.sys.types : ssize_t;
|
||
|
||
/+
|
||
Linux `getrandom()` emulation built upon `/dev/urandom`.
|
||
The fourth parameter (`uint flags`) is happily ignored.
|
||
+/
|
||
private ssize_t getrandom(
|
||
void* buf,
|
||
size_t buflen,
|
||
uint,
|
||
) @system nothrow @nogc
|
||
{
|
||
import core.stdc.stdio : fclose, fopen, fread;
|
||
|
||
auto blockDev = fopen(_pathLinuxSystemCSPRNG.ptr, "r");
|
||
if (blockDev is null)
|
||
assert(false, "System CSPRNG unavailable: `fopen(\"" ~ _pathLinuxSystemCSPRNG ~ "\")` failed.");
|
||
scope (exit) fclose(blockDev);
|
||
|
||
const bytesRead = fread(buf, 1, buflen, blockDev);
|
||
return bytesRead;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// `getrandom()` was introduced in Linux 3.17.
|
||
|
||
// Shim for missing bindings in druntime
|
||
version (none)
|
||
import core.sys.linux.sys.random : getrandom;
|
||
else
|
||
{
|
||
import core.sys.posix.sys.types : ssize_t;
|
||
private extern extern(C) ssize_t getrandom(
|
||
void* buf,
|
||
size_t buflen,
|
||
uint flags,
|
||
) @system nothrow @nogc;
|
||
}
|
||
}
|
||
}
|
||
|
||
version (Windows)
|
||
{
|
||
import std.internal.windows.bcrypt : bcryptGenRandom;
|
||
}
|
||
|
||
/**
|
||
A "good" seed for initializing random number engines. Initializing
|
||
with $(D_PARAM unpredictableSeed) makes engines generate different
|
||
random number sequences every run.
|
||
|
||
This function utilizes the system $(I cryptographically-secure pseudo-random
|
||
number generator (CSPRNG)) or $(I pseudo-random number generator (PRNG))
|
||
where available and implemented (currently `arc4random` on applicable BSD
|
||
systems, `getrandom` on Linux or `BCryptGenRandom` on Windows) to generate
|
||
“high quality” pseudo-random numbers – if possible.
|
||
As a consequence, calling it may block under certain circumstances (typically
|
||
during early boot when the system's entropy pool has not yet been
|
||
initialized).
|
||
|
||
On x86 CPU models which support the `RDRAND` instruction, that will be used
|
||
when no more specialized randomness source is implemented.
|
||
|
||
In the future, further platform-specific PRNGs may be incorporated.
|
||
|
||
Warning:
|
||
$(B This function must not be used for cryptographic purposes.)
|
||
Despite being implemented for certain targets, there are no guarantees
|
||
that it sources its randomness from a CSPRNG.
|
||
The implementation also includes a fallback option that provides very little
|
||
randomness and is used when no better source of randomness is available or
|
||
integrated on the target system.
|
||
As written earlier, this function only aims to provide randomness for seeding
|
||
ordinary (non-cryptographic) PRNG engines.
|
||
|
||
Returns:
|
||
A single unsigned integer seed value, different on each successive call
|
||
Note:
|
||
In general periodically 'reseeding' a PRNG does not improve its quality
|
||
and in some cases may harm it. For an extreme example the Mersenne
|
||
Twister has `2 ^^ 19937 - 1` distinct states but after `seed(uint)` is
|
||
called it can only be in one of `2 ^^ 32` distinct states regardless of
|
||
how excellent the source of entropy is.
|
||
*/
|
||
@property uint unpredictableSeed() @trusted nothrow @nogc
|
||
{
|
||
version (linux)
|
||
{
|
||
uint buffer;
|
||
|
||
/*
|
||
getrandom(2):
|
||
If the _urandom_ source has been initialized, reads of up to
|
||
256 bytes will always return as many bytes as requested and
|
||
will not be interrupted by signals. No such guarantees apply
|
||
for larger buffer sizes.
|
||
*/
|
||
static assert(buffer.sizeof <= 256);
|
||
|
||
const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))();
|
||
assert(status == buffer.sizeof);
|
||
|
||
return buffer;
|
||
}
|
||
else version (Windows)
|
||
{
|
||
uint result;
|
||
if (!bcryptGenRandom!uint(result))
|
||
{
|
||
version (none)
|
||
return fallbackSeed();
|
||
else
|
||
assert(false, "BCryptGenRandom() failed.");
|
||
}
|
||
return result;
|
||
}
|
||
else version (AnyARC4Random)
|
||
{
|
||
return arc4random();
|
||
}
|
||
else
|
||
{
|
||
version (InlineAsm_X86_Any)
|
||
{
|
||
import core.cpuid : hasRdrand;
|
||
if (hasRdrand)
|
||
{
|
||
uint result;
|
||
asm @nogc nothrow
|
||
{
|
||
db 0x0f, 0xc7, 0xf0; // rdrand EAX
|
||
jnc LnotUsingRdrand;
|
||
mov result, EAX;
|
||
// Some AMD CPUs shipped with bugs where RDRAND could fail
|
||
// but still set the carry flag to 1. In those cases the
|
||
// output will be -1.
|
||
cmp EAX, 0xffff_ffff;
|
||
jne LusingRdrand;
|
||
// If result was -1 verify RDAND isn't constantly returning -1.
|
||
db 0x0f, 0xc7, 0xf0;
|
||
jnc LusingRdrand;
|
||
cmp EAX, 0xffff_ffff;
|
||
je LnotUsingRdrand;
|
||
}
|
||
LusingRdrand:
|
||
return result;
|
||
}
|
||
LnotUsingRdrand:
|
||
}
|
||
return cast(uint) fallbackSeed();
|
||
}
|
||
}
|
||
|
||
/// ditto
|
||
template unpredictableSeed(UIntType)
|
||
if (isUnsigned!UIntType)
|
||
{
|
||
static if (is(UIntType == uint))
|
||
alias unpredictableSeed = .unpredictableSeed;
|
||
else static if (!is(Unqual!UIntType == UIntType))
|
||
alias unpredictableSeed = .unpredictableSeed!(Unqual!UIntType);
|
||
else
|
||
/// ditto
|
||
@property UIntType unpredictableSeed() @nogc nothrow @trusted
|
||
{
|
||
version (linux)
|
||
{
|
||
UIntType buffer;
|
||
|
||
/*
|
||
getrandom(2):
|
||
If the _urandom_ source has been initialized, reads of up to
|
||
256 bytes will always return as many bytes as requested and
|
||
will not be interrupted by signals. No such guarantees apply
|
||
for larger buffer sizes.
|
||
*/
|
||
static assert(buffer.sizeof <= 256);
|
||
|
||
const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))();
|
||
assert(status == buffer.sizeof);
|
||
|
||
return buffer;
|
||
}
|
||
else version (Windows)
|
||
{
|
||
UIntType result;
|
||
if (!bcryptGenRandom!UIntType(result))
|
||
{
|
||
version (none)
|
||
return fallbackSeed();
|
||
else
|
||
assert(false, "BCryptGenRandom() failed.");
|
||
}
|
||
return result;
|
||
}
|
||
else version (AnyARC4Random)
|
||
{
|
||
static if (UIntType.sizeof <= uint.sizeof)
|
||
{
|
||
return cast(UIntType) arc4random();
|
||
}
|
||
else
|
||
{
|
||
UIntType result = void;
|
||
arc4random_buf(&result, UIntType.sizeof);
|
||
return result;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
version (InlineAsm_X86_Any)
|
||
{
|
||
import core.cpuid : hasRdrand;
|
||
if (hasRdrand)
|
||
{
|
||
static if (UIntType.sizeof <= uint.sizeof)
|
||
{
|
||
uint result;
|
||
asm @nogc nothrow
|
||
{
|
||
db 0x0f, 0xc7, 0xf0; // rdrand EAX
|
||
jnc LnotUsingRdrand;
|
||
mov result, EAX;
|
||
// Some AMD CPUs shipped with bugs where RDRAND could fail
|
||
// but still set the carry flag to 1. In those cases the
|
||
// output will be -1.
|
||
cmp EAX, 0xffff_ffff;
|
||
jne LusingRdrand;
|
||
// If result was -1 verify RDAND isn't constantly returning -1.
|
||
db 0x0f, 0xc7, 0xf0;
|
||
jnc LusingRdrand;
|
||
cmp EAX, 0xffff_ffff;
|
||
je LnotUsingRdrand;
|
||
}
|
||
LusingRdrand:
|
||
return cast(UIntType) result;
|
||
}
|
||
else version (D_InlineAsm_X86_64)
|
||
{
|
||
ulong result;
|
||
asm @nogc nothrow
|
||
{
|
||
db 0x48, 0x0f, 0xc7, 0xf0; // rdrand RAX
|
||
jnc LnotUsingRdrand;
|
||
mov result, RAX;
|
||
// Some AMD CPUs shipped with bugs where RDRAND could fail
|
||
// but still set the carry flag to 1. In those cases the
|
||
// output will be -1.
|
||
cmp RAX, 0xffff_ffff_ffff_ffff;
|
||
jne LusingRdrand;
|
||
// If result was -1 verify RDAND isn't constantly returning -1.
|
||
db 0x48, 0x0f, 0xc7, 0xf0;
|
||
jnc LusingRdrand;
|
||
cmp RAX, 0xffff_ffff_ffff_ffff;
|
||
je LnotUsingRdrand;
|
||
}
|
||
LusingRdrand:
|
||
return result;
|
||
}
|
||
else
|
||
{
|
||
uint resultLow, resultHigh;
|
||
asm @nogc nothrow
|
||
{
|
||
db 0x0f, 0xc7, 0xf0; // rdrand EAX
|
||
jnc LnotUsingRdrand;
|
||
mov resultLow, EAX;
|
||
db 0x0f, 0xc7, 0xf0; // rdrand EAX
|
||
jnc LnotUsingRdrand;
|
||
mov resultHigh, EAX;
|
||
}
|
||
if (resultLow != uint.max || resultHigh != uint.max) // Protect against AMD RDRAND bug.
|
||
return ((cast(ulong) resultHigh) << 32) ^ resultLow;
|
||
}
|
||
}
|
||
LnotUsingRdrand:
|
||
}
|
||
return cast(UIntType) fallbackSeed();
|
||
}
|
||
}
|
||
}
|
||
|
||
///
|
||
@safe @nogc 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 @nogc 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 nothrow @nogc
|
||
{
|
||
static Random result;
|
||
static bool initialized;
|
||
if (!initialized)
|
||
{
|
||
static if (isSeedable!(Random, ulong))
|
||
result.seed(unpredictableSeed!ulong); // Avoid unnecessary copy.
|
||
else static if (is(Random : MersenneTwisterEngine!Params, Params...))
|
||
initMTEngine(result);
|
||
else static if (isSeedable!(Random, uint))
|
||
result.seed(unpredictableSeed!uint); // Avoid unnecessary copy.
|
||
else
|
||
result = Random(unpredictableSeed);
|
||
initialized = true;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
///
|
||
@safe nothrow @nogc unittest
|
||
{
|
||
import std.algorithm.iteration : sum;
|
||
import std.range : take;
|
||
auto rnd = rndGen;
|
||
assert(rnd.take(3).sum > 0);
|
||
}
|
||
|
||
/+
|
||
Initialize a 32-bit MersenneTwisterEngine from 64 bits of entropy.
|
||
This is private and accepts no seed as a parameter, freeing the internal
|
||
implementaton from any need for stability across releases.
|
||
+/
|
||
private void initMTEngine(MTEngine)(scope ref MTEngine mt)
|
||
if (is(MTEngine : MersenneTwisterEngine!Params, Params...))
|
||
{
|
||
pragma(inline, false); // Called no more than once per thread by rndGen.
|
||
ulong seed = unpredictableSeed!ulong;
|
||
static if (is(typeof(mt.seed(seed))))
|
||
{
|
||
mt.seed(seed);
|
||
}
|
||
else
|
||
{
|
||
alias UIntType = typeof(mt.front());
|
||
if (seed == 0) seed = -1; // Any number but 0 is fine.
|
||
uint s0 = cast(uint) seed;
|
||
uint s1 = cast(uint) (seed >> 32);
|
||
foreach (ref e; mt.state.data)
|
||
{
|
||
//http://xoshiro.di.unimi.it/xoroshiro64starstar.c
|
||
const tmp = s0 * 0x9E3779BB;
|
||
e = ((tmp << 5) | (tmp >> (32 - 5))) * 5;
|
||
static if (MTEngine.max != UIntType.max) { e &= MTEngine.max; }
|
||
|
||
const tmp1 = s0 ^ s1;
|
||
s0 = ((s0 << 26) | (s0 >> (32 - 26))) ^ tmp1 ^ (tmp1 << 9);
|
||
s1 = (tmp1 << 13) | (tmp1 >> (32 - 13));
|
||
}
|
||
|
||
mt.state.index = mt.state.data.length - 1;
|
||
// double popFront() to guarantee both `mt.state.z`
|
||
// and `mt.state.front` are derived from the newly
|
||
// set values in `mt.state.data`.
|
||
mt.popFront();
|
||
mt.popFront();
|
||
}
|
||
}
|
||
|
||
/**
|
||
Generates a number between `a` and `b`. The `boundaries`
|
||
parameter controls the shape of the interval (open vs. closed on
|
||
either side). Valid values for `boundaries` are `"[]"`, $(D
|
||
"$(LPAREN)]"), `"[$(RPAREN)"`, and `"()"`. The default interval
|
||
is closed to the left and open to the right. The version that does not
|
||
take `urng` uses the default generator `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 `rndGen`
|
||
|
||
Returns:
|
||
A single random variate drawn from the _uniform distribution
|
||
between `a` and `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 rnd = Random(unpredictableSeed);
|
||
|
||
// Generate an integer in [0, 1023]
|
||
auto a = uniform(0, 1024, rnd);
|
||
assert(0 <= a && a < 1024);
|
||
|
||
// Generate a float in [0, 1)
|
||
auto b = uniform(0.0f, 1.0f, rnd);
|
||
assert(0 <= b && b < 1);
|
||
|
||
// Generate a float in [0, 1]
|
||
b = uniform!"[]"(0.0f, 1.0f, rnd);
|
||
assert(0 <= b && b <= 1);
|
||
|
||
// Generate a float in (0, 1)
|
||
b = uniform!"()"(0.0f, 1.0f, rnd);
|
||
assert(0 < b && b < 1);
|
||
}
|
||
|
||
/// Create an array of random numbers using range functions and UFCS
|
||
@safe unittest
|
||
{
|
||
import std.array : array;
|
||
import std.range : generate, takeExactly;
|
||
|
||
int[] arr = generate!(() => uniform(0, 100)).takeExactly(10).array;
|
||
assert(arr.length == 10);
|
||
assert(arr[0] >= 0 && arr[0] < 100);
|
||
}
|
||
|
||
@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.operations : nextafter;
|
||
NumberType _a = nextafter(cast(NumberType) a, NumberType.infinity);
|
||
}
|
||
else
|
||
{
|
||
NumberType _a = a;
|
||
}
|
||
static if (boundaries[1] == ')')
|
||
{
|
||
import std.math.operations : 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))`
|
||
+/
|
||
/// ditto
|
||
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;
|
||
import std.meta : AliasSeq;
|
||
import std.range.primitives : isForwardRange;
|
||
import std.traits : isIntegral, isSomeChar;
|
||
|
||
auto gen = Mt19937(123_456_789);
|
||
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);
|
||
|
||
static foreach (T; 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);
|
||
|
||
static foreach (T; 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 an unsigned integer in the half-open range `[0, k)`.
|
||
Non-public because we locally guarantee `k > 0`.
|
||
|
||
Params:
|
||
k = unsigned exclusive upper bound; caller guarantees this is non-zero
|
||
rng = random number generator to use
|
||
|
||
Returns:
|
||
Pseudo-random unsigned integer strictly less than `k`.
|
||
+/
|
||
private UInt _uniformIndex(UniformRNG, UInt = size_t)(const UInt k, ref UniformRNG rng)
|
||
if (isUnsigned!UInt && isUniformRNG!UniformRNG)
|
||
{
|
||
alias ResultType = UInt;
|
||
alias UpperType = Unsigned!(typeof(k - 0));
|
||
alias upperDist = k;
|
||
|
||
assert(upperDist != 0);
|
||
|
||
// For backwards compatibility use same algorithm as uniform(0, k, rng).
|
||
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) offset;
|
||
}
|
||
|
||
pure @safe unittest
|
||
{
|
||
// For backwards compatibility check that _uniformIndex(k, rng)
|
||
// has the same result as uniform(0, k, rng).
|
||
auto rng1 = Xorshift(123_456_789);
|
||
auto rng2 = rng1.save();
|
||
const size_t k = (1U << 31) - 1;
|
||
assert(_uniformIndex(k, rng1) == uniform(0, k, rng2));
|
||
}
|
||
|
||
/**
|
||
Generates a uniformly-distributed number in the range $(D [T.min,
|
||
T.max]) for any integral or character type `T`. If no random
|
||
number generator is passed, uses the default `rndGen`.
|
||
|
||
If an `enum` is used as type, the random variate is drawn with
|
||
equal probability from any of the possible values of the enum `E`.
|
||
|
||
Params:
|
||
urng = (optional) random number generator to use;
|
||
if not specified, defaults to `rndGen`
|
||
|
||
Returns:
|
||
Random variate drawn from the _uniform distribution across all
|
||
possible values of the integral, character or enum type `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(immutable T == immutable dchar))
|
||
{
|
||
return uniform!"[]"(T.min, T.max, urng);
|
||
}
|
||
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
|
||
{
|
||
auto rnd = MinstdRand0(42);
|
||
|
||
assert(rnd.uniform!ubyte == 102);
|
||
assert(rnd.uniform!ulong == 4838462006927449017);
|
||
|
||
enum Fruit { apple, mango, pear }
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert(rnd.uniform!Fruit == Fruit.mango);
|
||
}
|
||
|
||
@safe unittest
|
||
{
|
||
// https://issues.dlang.org/show_bug.cgi?id=21383
|
||
auto rng1 = Xorshift32(123456789);
|
||
auto rng2 = rng1.save;
|
||
assert(rng1.uniform!dchar == rng2.uniform!dchar);
|
||
// https://issues.dlang.org/show_bug.cgi?id=21384
|
||
assert(rng1.uniform!(const shared dchar) <= dchar.max);
|
||
// https://issues.dlang.org/show_bug.cgi?id=8671
|
||
double t8671 = 1.0 - uniform(0.0, 1.0);
|
||
}
|
||
|
||
@safe unittest
|
||
{
|
||
static 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);
|
||
}
|
||
}}
|
||
}
|
||
|
||
/// ditto
|
||
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 = 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
|
||
* `T` in the range [0, 1$(RPAREN). If no random number generator is
|
||
* specified, the default RNG `rndGen` will be used as the source
|
||
* of randomness.
|
||
*
|
||
* `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 `rndGen`
|
||
*
|
||
* Returns:
|
||
* Floating-point random variate of type `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);
|
||
}
|
||
do
|
||
{
|
||
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();
|
||
|
||
static if (isIntegral!R && T.mant_dig >= (8 * 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 @nogc unittest
|
||
{
|
||
import std.math.operations : feqrel;
|
||
|
||
auto rnd = MinstdRand0(42);
|
||
|
||
// Generate random numbers in the range in the range [0, 1)
|
||
auto u1 = uniform01(rnd);
|
||
assert(u1 >= 0 && u1 < 1);
|
||
|
||
auto u2 = rnd.uniform01!float;
|
||
assert(u2 >= 0 && u2 < 1);
|
||
|
||
// Confirm that the random values with the initial seed 42 are 0.000328707 and 0.524587
|
||
assert(u1.feqrel(0.000328707) > 20);
|
||
assert(u2.feqrel(0.524587) > 20);
|
||
}
|
||
|
||
@safe @nogc unittest
|
||
{
|
||
import std.meta;
|
||
static foreach (UniformRNG; PseudoRngTypes)
|
||
{{
|
||
|
||
static foreach (T; std.meta.AliasSeq!(float, double, real))
|
||
{{
|
||
UniformRNG rng = UniformRNG(123_456_789);
|
||
|
||
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 `n`, i.e., an
|
||
array of size `n` of positive numbers of type `F` that sum to
|
||
`1`. If `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.iteration : reduce;
|
||
import std.math.operations : isClose;
|
||
|
||
auto a = uniformDistribution(5);
|
||
assert(a.length == 5);
|
||
assert(isClose(reduce!"a + b"(a), 1));
|
||
|
||
a = uniformDistribution(10, a);
|
||
assert(a.length == 10);
|
||
assert(isClose(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)(Range range, ref RandomGen urng)
|
||
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)];
|
||
}
|
||
|
||
/// ditto
|
||
auto ref choice(Range)(Range range)
|
||
{
|
||
return choice(range, rndGen);
|
||
}
|
||
|
||
/// ditto
|
||
auto ref choice(Range, RandomGen = Random)(ref Range range, ref RandomGen urng)
|
||
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)];
|
||
}
|
||
|
||
/// ditto
|
||
auto ref choice(Range)(ref Range range)
|
||
{
|
||
return choice(range, rndGen);
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
auto rnd = MinstdRand0(42);
|
||
|
||
auto elem = [1, 2, 3, 4, 5].choice(rnd);
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert(elem == 3);
|
||
}
|
||
|
||
@safe unittest
|
||
{
|
||
import std.algorithm.searching : canFind;
|
||
|
||
static 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");
|
||
}
|
||
|
||
@safe unittest // https://issues.dlang.org/show_bug.cgi?id=18631
|
||
{
|
||
auto rng = MinstdRand0(42);
|
||
const a = [0,1,2];
|
||
const(int[]) b = [0, 1, 2];
|
||
auto x = choice(a);
|
||
auto y = choice(b);
|
||
auto z = choice(cast(const)[1, 2, 3]);
|
||
auto x1 = choice(a, rng);
|
||
auto y1 = choice(b, rng);
|
||
auto z1 = choice(cast(const)[1, 2, 3], rng);
|
||
}
|
||
|
||
@safe unittest // Ref range (https://issues.dlang.org/show_bug.cgi?id=18631 PR)
|
||
{
|
||
struct TestRange
|
||
{
|
||
int x;
|
||
ref int front() return {return x;}
|
||
ref int back() return {return x;}
|
||
void popFront() {}
|
||
void popBack() {}
|
||
bool empty = false;
|
||
TestRange save() {return this;}
|
||
size_t length = 10;
|
||
alias opDollar = length;
|
||
ref int opIndex(size_t i) return {return x;}
|
||
}
|
||
|
||
TestRange r = TestRange(10);
|
||
int* s = &choice(r);
|
||
}
|
||
|
||
/**
|
||
Shuffles elements of `r` using `gen` as a shuffler. `r` must be
|
||
a random-access range with length. If no RNG is specified, `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 `rndGen`
|
||
Returns:
|
||
The shuffled random-access range.
|
||
*/
|
||
|
||
Range randomShuffle(Range, RandomGen)(Range r, ref RandomGen gen)
|
||
if (isRandomAccessRange!Range && isUniformRNG!RandomGen)
|
||
{
|
||
import std.algorithm.mutation : swapAt;
|
||
const n = r.length;
|
||
foreach (i; 0 .. n)
|
||
{
|
||
r.swapAt(i, i + _uniformIndex(n - i, gen));
|
||
}
|
||
return r;
|
||
}
|
||
|
||
/// ditto
|
||
Range randomShuffle(Range)(Range r)
|
||
if (isRandomAccessRange!Range)
|
||
{
|
||
return randomShuffle(r, rndGen);
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
auto rnd = MinstdRand0(42);
|
||
|
||
auto arr = [1, 2, 3, 4, 5].randomShuffle(rnd);
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert(arr == [3, 5, 2, 4, 1]);
|
||
}
|
||
|
||
@safe unittest
|
||
{
|
||
int[10] sa = void;
|
||
int[10] sb = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||
import std.algorithm.sorting : sort;
|
||
foreach (RandomGen; PseudoRngTypes)
|
||
{
|
||
sa[] = sb[];
|
||
auto a = sa[];
|
||
auto b = sb[];
|
||
auto gen = RandomGen(123_456_789);
|
||
randomShuffle(a, gen);
|
||
sort(a);
|
||
assert(a == b);
|
||
randomShuffle(a);
|
||
sort(a);
|
||
assert(a == b);
|
||
}
|
||
// For backwards compatibility verify randomShuffle(r, gen)
|
||
// is equivalent to partialShuffle(r, 0, r.length, gen).
|
||
auto gen1 = Xorshift(123_456_789);
|
||
auto gen2 = gen1.save();
|
||
sa[] = sb[];
|
||
// @nogc std.random.randomShuffle.
|
||
// https://issues.dlang.org/show_bug.cgi?id=19156
|
||
() @nogc nothrow pure { randomShuffle(sa[], gen1); }();
|
||
partialShuffle(sb[], sb.length, gen2);
|
||
assert(sa[] == sb[]);
|
||
}
|
||
|
||
// https://issues.dlang.org/show_bug.cgi?id=18501
|
||
@safe unittest
|
||
{
|
||
import std.algorithm.comparison : among;
|
||
auto r = randomShuffle([0,1]);
|
||
assert(r.among([0,1],[1,0]));
|
||
}
|
||
|
||
/**
|
||
Partially shuffles the elements of `r` such that upon returning $(D r[0 .. n])
|
||
is a random subset of `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
|
||
`partialShuffle` returns will not be independent of their order before
|
||
`partialShuffle` was called.
|
||
|
||
`r` must be a random-access range with length. `n` must be less than
|
||
or equal to `r.length`. If no RNG is specified, `rndGen` will be used.
|
||
|
||
Params:
|
||
r = random-access range whose elements are to be shuffled
|
||
n = number of elements of `r` to shuffle (counting from the beginning);
|
||
must be less than `r.length`
|
||
gen = (optional) random number generator to use; if not
|
||
specified, defaults to `rndGen`
|
||
Returns:
|
||
The shuffled random-access range.
|
||
*/
|
||
Range 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));
|
||
}
|
||
return r;
|
||
}
|
||
|
||
/// ditto
|
||
Range partialShuffle(Range)(Range r, in size_t n)
|
||
if (isRandomAccessRange!Range)
|
||
{
|
||
return partialShuffle(r, n, rndGen);
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
auto rnd = MinstdRand0(42);
|
||
|
||
auto arr = [1, 2, 3, 4, 5, 6];
|
||
arr = arr.dup.partialShuffle(1, rnd);
|
||
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert(arr == [2, 1, 3, 4, 5, 6]); // 1<->2
|
||
|
||
arr = arr.dup.partialShuffle(2, rnd);
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert(arr == [1, 4, 3, 2, 5, 6]); // 1<->2, 2<->4
|
||
|
||
arr = arr.dup.partialShuffle(3, rnd);
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert(arr == [5, 4, 6, 2, 1, 3]); // 1<->5, 2<->4, 3<->6
|
||
}
|
||
|
||
@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));
|
||
}
|
||
}
|
||
|
||
/**
|
||
Get a random index into a list of weights corresponding to each index
|
||
|
||
Similar to rolling a die with relative probabilities stored in `proportions`.
|
||
Returns the index in `proportions` that was chosen.
|
||
|
||
Note:
|
||
Usually, dice are 'fair', meaning that each side has equal probability
|
||
to come up, in which case `1 + uniform(0, 6)` can simply be used.
|
||
In future Phobos versions, this function might get renamed to something like
|
||
`weightedChoice` to avoid confusion.
|
||
|
||
Params:
|
||
rnd = (optional) random number generator to use; if not
|
||
specified, defaults to `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, ... `proportions.length` - 1], with the probability
|
||
of getting an individual index value `i` being proportional to
|
||
`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 d6 = 1 + dice(1, 1, 1, 1, 1, 1); // fair dice roll
|
||
auto d6b = 1 + dice(2, 1, 1, 1, 1, 1); // double the chance to roll '1'
|
||
|
||
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
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
auto rnd = MinstdRand0(42);
|
||
auto z = rnd.dice(70, 20, 10);
|
||
assert(z == 0);
|
||
z = rnd.dice(30, 20, 40, 10);
|
||
assert(z == 2);
|
||
}
|
||
|
||
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");
|
||
}
|
||
do
|
||
{
|
||
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 = Xorshift(123_456_789);
|
||
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);
|
||
}
|
||
|
||
/+ @nogc bool array designed for RandomCover.
|
||
- constructed with an invariable length
|
||
- small length means 0 alloc and bit field (if up to 32(x86) or 64(x64) choices to cover)
|
||
- bigger length means non-GC heap allocation(s) and dealloc. +/
|
||
private struct RandomCoverChoices
|
||
{
|
||
private size_t* buffer;
|
||
private immutable size_t _length;
|
||
private immutable bool hasPackedBits;
|
||
private enum BITS_PER_WORD = typeof(buffer[0]).sizeof * 8;
|
||
|
||
void opAssign(T)(T) @disable;
|
||
|
||
this(this) pure nothrow @nogc @trusted
|
||
{
|
||
import core.stdc.string : memcpy;
|
||
import std.internal.memory : enforceMalloc;
|
||
|
||
if (!hasPackedBits && buffer !is null)
|
||
{
|
||
const nBytesToAlloc = size_t.sizeof * (_length / BITS_PER_WORD + int(_length % BITS_PER_WORD != 0));
|
||
void* nbuffer = enforceMalloc(nBytesToAlloc);
|
||
buffer = cast(size_t*) memcpy(nbuffer, buffer, nBytesToAlloc);
|
||
}
|
||
}
|
||
|
||
this(size_t numChoices) pure nothrow @nogc @trusted
|
||
{
|
||
import std.internal.memory : enforceCalloc;
|
||
|
||
_length = numChoices;
|
||
hasPackedBits = _length <= size_t.sizeof * 8;
|
||
if (!hasPackedBits)
|
||
{
|
||
const nWordsToAlloc = _length / BITS_PER_WORD + int(_length % BITS_PER_WORD != 0);
|
||
buffer = cast(size_t*) enforceCalloc(nWordsToAlloc, BITS_PER_WORD / 8);
|
||
}
|
||
}
|
||
|
||
size_t length() const pure nothrow @nogc @safe @property {return _length;}
|
||
|
||
~this() pure nothrow @nogc @trusted
|
||
{
|
||
import core.memory : pureFree;
|
||
|
||
if (!hasPackedBits && buffer !is null)
|
||
pureFree(buffer);
|
||
}
|
||
|
||
bool opIndex(size_t index) const pure nothrow @nogc @trusted
|
||
{
|
||
assert(index < _length);
|
||
import core.bitop : bt;
|
||
if (!hasPackedBits)
|
||
return cast(bool) bt(buffer, index);
|
||
else
|
||
return ((cast(size_t) buffer) >> index) & size_t(1);
|
||
}
|
||
|
||
void opIndexAssign(bool value, size_t index) pure nothrow @nogc @trusted
|
||
{
|
||
assert(index < _length);
|
||
if (!hasPackedBits)
|
||
{
|
||
import core.bitop : btr, bts;
|
||
if (value)
|
||
bts(buffer, index);
|
||
else
|
||
btr(buffer, index);
|
||
}
|
||
else
|
||
{
|
||
if (value)
|
||
(*cast(size_t*) &buffer) |= size_t(1) << index;
|
||
else
|
||
(*cast(size_t*) &buffer) &= ~(size_t(1) << index);
|
||
}
|
||
}
|
||
}
|
||
|
||
@safe @nogc nothrow unittest
|
||
{
|
||
static immutable lengths = [3, 32, 65, 256];
|
||
foreach (length; lengths)
|
||
{
|
||
RandomCoverChoices c = RandomCoverChoices(length);
|
||
assert(c.hasPackedBits == (length <= size_t.sizeof * 8));
|
||
c[0] = true;
|
||
c[2] = true;
|
||
assert(c[0]);
|
||
assert(!c[1]);
|
||
assert(c[2]);
|
||
c[0] = false;
|
||
c[1] = true;
|
||
c[2] = false;
|
||
assert(!c[0]);
|
||
assert(c[1]);
|
||
assert(!c[2]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
Covers a given range `r` in a random manner, i.e. goes through each
|
||
element of `r` once and only once, just in a random order. `r`
|
||
must be a random-access range with length.
|
||
|
||
If no random number generator is passed to `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 `rndGen`
|
||
|
||
Returns:
|
||
Range whose elements consist of the elements of `r`,
|
||
in random order. Will be a forward range if both `r` and
|
||
`rng` are forward ranges, an
|
||
$(REF_ALTTEXT input range, isInputRange, std,range,primitives) otherwise.
|
||
*/
|
||
struct RandomCover(Range, UniformRNG = void)
|
||
if (isRandomAccessRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
|
||
{
|
||
private Range _input;
|
||
private RandomCoverChoices _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 = RandomCoverChoices(_input.length);
|
||
if (_input.empty)
|
||
{
|
||
_isEmpty = true;
|
||
}
|
||
else
|
||
{
|
||
_current = _uniformIndex(_chosen.length, rndGen);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
private UniformRNG _rng;
|
||
|
||
this(Range input, ref UniformRNG rng)
|
||
{
|
||
_input = input;
|
||
_rng = rng;
|
||
_chosen = RandomCoverChoices(_input.length);
|
||
if (_input.empty)
|
||
{
|
||
_isEmpty = true;
|
||
}
|
||
else
|
||
{
|
||
_current = _uniformIndex(_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 = _uniformIndex(k, rndGen) == 0;
|
||
}
|
||
else
|
||
{
|
||
auto chooseMe = _uniformIndex(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() const { 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.comparison : equal;
|
||
import std.range : iota;
|
||
auto rnd = MinstdRand0(42);
|
||
|
||
version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
|
||
assert(10.iota.randomCover(rnd).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5]));
|
||
}
|
||
|
||
@safe unittest // cover RandomCoverChoices postblit for heap storage
|
||
{
|
||
import std.array : array;
|
||
import std.range : iota;
|
||
auto a = 1337.iota.randomCover().array;
|
||
assert(a.length == 1337);
|
||
}
|
||
|
||
@nogc nothrow pure @safe unittest
|
||
{
|
||
// Optionally @nogc std.random.randomCover
|
||
// https://issues.dlang.org/show_bug.cgi?id=14001
|
||
auto rng = Xorshift(123_456_789);
|
||
static immutable int[] sa = [1, 2, 3, 4, 5];
|
||
auto r = randomCover(sa, rng);
|
||
assert(!r.empty);
|
||
const x = r.front;
|
||
r.popFront();
|
||
assert(!r.empty);
|
||
const y = r.front;
|
||
assert(x != y);
|
||
}
|
||
|
||
@safe unittest
|
||
{
|
||
import std.algorithm;
|
||
import std.conv;
|
||
int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
|
||
int[] c;
|
||
static 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(123_456_789);
|
||
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(987_654_321));
|
||
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
|
||
{
|
||
// https://issues.dlang.org/show_bug.cgi?id=12589
|
||
int[] r = [];
|
||
auto rc = randomCover(r);
|
||
assert(rc.length == 0);
|
||
assert(rc.empty);
|
||
|
||
// https://issues.dlang.org/show_bug.cgi?id=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 `r`, containing exactly `n`
|
||
elements. The order of elements is the same as in the original
|
||
range. The total length of `r` must be known. If `total` is
|
||
passed in, the total number of sample is considered to be $(D
|
||
total). Otherwise, `RandomSample` uses `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 `r` and/or the parameter
|
||
`total` (if provided)
|
||
total = (semi-optional) number of elements of `r`
|
||
from which to select the sample (counting from
|
||
the beginning); must be less than or equal to
|
||
the total number of elements in `r` itself.
|
||
May be omitted if `r` has the `.length`
|
||
property and the sample is to be drawn from
|
||
all elements of `r`.
|
||
rng = (optional) random number generator to use;
|
||
if not specified, defaults to `rndGen`
|
||
|
||
Returns:
|
||
Range whose elements consist of a randomly selected subset of
|
||
the elements of `r`, in the same order as these elements
|
||
appear in `r` itself. Will be a forward range if both `r`
|
||
and `rng` are forward ranges, an input range otherwise.
|
||
|
||
`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 `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 `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 `randomSample`, the
|
||
thread-global RNG rndGen will be used internally.
|
||
*/
|
||
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 scope 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 scope 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)
|
||
{
|
||
static if (is(typeof(((const UniformRNG* p) => (*p).save)(null)) : UniformRNG)
|
||
&& is(typeof(((const Range* p) => (*p).save)(null)) : Range))
|
||
{
|
||
@property typeof(this) save() const
|
||
{
|
||
auto ret = RandomSample.init;
|
||
foreach (fieldIndex, ref val; this.tupleof)
|
||
{
|
||
static if (is(typeof(val) == const(Range)) || is(typeof(val) == const(UniformRNG)))
|
||
ret.tupleof[fieldIndex] = val.save;
|
||
else
|
||
ret.tupleof[fieldIndex] = val;
|
||
}
|
||
return ret;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
@property typeof(this) save()
|
||
{
|
||
auto ret = this;
|
||
ret._input = _input.save;
|
||
ret._rng = _rng.save;
|
||
return ret;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Ditto
|
||
@property size_t length() const
|
||
{
|
||
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.traits : isNaN;
|
||
import std.math.rounding : 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);
|
||
}
|
||
|
||
///
|
||
@safe unittest
|
||
{
|
||
import std.algorithm.comparison : equal;
|
||
import std.range : iota;
|
||
auto rnd = MinstdRand0(42);
|
||
assert(10.iota.randomSample(3, rnd).equal([7, 8, 9]));
|
||
}
|
||
|
||
@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);
|
||
|
||
const(int)[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
|
||
|
||
foreach (UniformRNG; PseudoRngTypes)
|
||
(){ // avoid workaround optimizations for large functions
|
||
// https://issues.dlang.org/show_bug.cgi?id=2396
|
||
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(987_654_321));
|
||
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(654_321_987));
|
||
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!(const(int)[], UniformRNG)
|
||
(a, 5, UniformRNG(321_987_654));
|
||
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!(const(int)[], UniformRNG)
|
||
(a, 5, UniformRNG(789_123_456));
|
||
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: https://issues.dlang.org/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 .. 50_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 < 150, text("1: ", count1, " > 150."));
|
||
assert(2_200 < count99, text("99: ", count99, " < 2200."));
|
||
assert(count99 < 2_800, text("99: ", count99, " > 2800."));
|
||
}
|
||
|
||
/* 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);
|
||
// https://issues.dlang.org/show_bug.cgi?id=15853
|
||
auto sample2 = ((const ref typeof(sample1) a) => a.save)(sample1);
|
||
assert(sample1.array() == sample2.array());
|
||
}
|
||
|
||
// https://issues.dlang.org/show_bug.cgi?id=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);
|
||
}
|
||
}();
|
||
}
|