Issue 18715 - Non-documented unittests should not use unpredictableSeed or default Random alias
merged-on-behalf-of: Nathan Sashihara <n8sh@users.noreply.github.com>
The fix for 18631 broke code. Ranges cannot be marked with const or
inout in generic code. The range API does not require that any functions
be const or inout. In this case, with the changes, choice requires that
length and opSlice be const, breaking any code that uses a range with
choice that does not have a const length or opSlice (which is going to
be a large percentage of ranges with those functions which aren't
arrays).
XorshiftEngine.min is defined as 0 regardless of template parameters
but an XorshiftEngine cannot produce a value of zero if its internal
state has the same number of bits as the output element type.
Random choice default argument was deciding the type of the
template. I'm not sure if this is a deeper bug, but it can easily be
fixed with this patch which simply overloads the function.
Also added a unittest which would have failed under the old code.
As in XorshiftEngine, sanitizing the seed is more useful than throwing
an exception. The original check was broken because it checked the seed
before taking the modulus. Making this change allows unpredictableSeed
to be marked nothrow and @nogc.
As with the earlier test of range-based seeding for the 32-bit Mt19937,
this test is added purely in order to ensure consistency of behaviour in
future. It does not appear to match the behaviour of the superficially
similar sequence-based seeding available in the C++11 <random> version
of the Mersenne Twister.
It does however match the behaviour of the `MersenneTwisterEngine`
implementation available in `hap.random`, which was derived from the
`Boost.Random` implementation, but whose range-based seeding was copied
from Phobos. This would suggest that any divergence with C++11 is down
entirely to the seeding mechanism.
The word size for `MersenneTwisterEngine` is determined by the template
parameter `w`, not `UIntType`. For example, a generator with identical
parameters to the standard `Mt19937` but with a `ulong` wordtype should
not produce different results to the standard `uint`-based generator.
This patch adds unittests for the first and 10_000'th values of the
sequences generated by a variety of Mersenne Twister implementations
with non-standard template parameter values.
The values in this unittest can be validated by comparison to C++11 by
compiling and running the following C++ program:
/****************************************************************/
template<class UIntType, size_t w>
void mt_result ()
{
std::mersenne_twister_engine<
UIntType, w, 624, 397, 31,
0x9908b0df, 11, 0xffffffff, 7,
0x9d2c5680, 15,
0xefc60000, 18, 1812433253> gen;
gen.seed(std::mt19937::default_seed);
std::cout << gen() << std::endl;
for (int i = 0; i < 9998; ++i)
gen();
std::cout << gen() << std::endl;
std::cout << std::endl;
}
int main ()
{
mt_result<uint32_t, 32>();
mt_result<uint64_t, 32>();
mt_result<uint64_t, 48>();
mt_result<uint64_t, 64>();
}
/****************************************************************/
Note that the `for` loop in this example advances the generator 9998
times compared to the D unittest's `popFrontN(9999)` because the first
`gen()` call already advances the generator once.
With the required tempering parameter `d` introduced by the previous
patch, we can now introduce the standard 64-bit implementation of the
Mersenne Twister. See https://en.wikipedia.org/wiki/Mersenne_Twister
for an explanation of the chosen constants.
Some minimal unittests have been added similar to those already present
for the 32-bit `Mt19937`. These can be verified by comparison to C++11
by compiling and running the following C++ program:
/****************************************************************/
int main ()
{
static_assert(std::mt19937_64::default_seed == 5489,
"Default seed does not match Phobos!");
std::mt19937_64 gen(std::mt19937_64::default_seed);
std::cout << gen() << std::endl;
for (int i = 0; i < 9998; ++i) {
gen();
}
std::cout << gen() << std::endl;
}
/****************************************************************/
Note that the `for` loop in this example advances the generator 9998
times compared to the D unittest's `popFrontN(9999)` because the first
`gen()` call already advances the generator once.
Fixes Issue #10900 <https://issues.dlang.org/show_bug.cgi?id=10900>.