The OpenD Programming Language

1 /++
2 The Mersenne Twister generator.
3 
4 Copyright: Copyright Andrei Alexandrescu 2008 - 2009, Ilya Yaroshenko 2016-.
5 License:    $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 Authors: $(HTTP erdani.org, Andrei Alexandrescu) Ilya Yaroshenko (rework)
7 +/
8 module mir.random.engine.mersenne_twister;
9 
10 import std.traits;
11 
12 /++
13 The $(LUCKY Mersenne Twister) generator.
14 +/
15 struct MersenneTwisterEngine(UIntType, size_t w, size_t n, size_t m, size_t r,
16                              UIntType a, size_t u, UIntType d, size_t s,
17                              UIntType b, size_t t,
18                              UIntType c, size_t l, UIntType f)
19     if (isUnsigned!UIntType)
20 {
21     ///
22     enum isRandomEngine = true;
23 
24     static assert(0 < w && w <= UIntType.sizeof * 8);
25     static assert(1 <= m && m <= n);
26     static assert(0 <= r && 0 <= u && 0 <= s && 0 <= t && 0 <= l);
27     static assert(r <= w && u <= w && s <= w && t <= w && l <= w);
28     static assert(0 <= a && 0 <= b && 0 <= c);
29 
30     @disable this();
31     @disable this(this);
32 
33     /// Largest generated value.
34     enum UIntType max = UIntType.max >> (UIntType.sizeof * 8u - w);
35     static assert(a <= max && b <= max && c <= max && f <= max);
36 
37     private enum UIntType lowerMask = (cast(UIntType) 1u << r) - 1;
38     private enum UIntType upperMask = ~lowerMask & max;
39 
40     /**
41     Parameters for the generator.
42     */
43     enum size_t   wordSize   = w;
44     enum size_t   stateSize  = n; /// ditto
45     enum size_t   shiftSize  = m; /// ditto
46     enum size_t   maskBits   = r; /// ditto
47     enum UIntType xorMask    = a; /// ditto
48     enum size_t   temperingU = u; /// ditto
49     enum UIntType temperingD = d; /// ditto
50     enum size_t   temperingS = s; /// ditto
51     enum UIntType temperingB = b; /// ditto
52     enum size_t   temperingT = t; /// ditto
53     enum UIntType temperingC = c; /// ditto
54     enum size_t   temperingL = l; /// ditto
55     enum UIntType initializationMultiplier = f; /// ditto
56 
57 
58     /// The default seed value.
59     enum UIntType defaultSeed = 5489;
60 
61     /++
62     Current reversed payload index with initial value equals to `n-1`
63     +/
64     size_t index = void;
65 
66     private UIntType _z = void;
67 
68     /++
69     Reversed(!) payload.
70     +/
71     UIntType[n] data = void;
72 
73     /*
74      * Marker indicating it's safe to construct from void
75      * (i.e. the constructor doesn't depend on the struct
76      * being in an initially valid state).
77      * Non-public because we don't want to commit to this
78      * design.
79      */
80     package enum bool _isVoidInitOkay = true;
81 
82     /++
83     Constructs a MersenneTwisterEngine object.
84     +/
85     this(UIntType value) @safe pure nothrow @nogc
86     {
87         static if (max == UIntType.max)
88             data[$-1] = value;
89         else
90             data[$-1] = value & max;
91         foreach_reverse (size_t i, ref e; data[0 .. $-1])
92         {
93             e = f * (data[i + 1] ^ (data[i + 1] >> (w - 2))) + cast(UIntType)(n - (i + 1));
94             static if (max != UIntType.max)
95                 e &= max;
96         }
97         index = n-1;
98         opCall();
99     }
100 
101     /++
102     Constructs a MersenneTwisterEngine object.
103 
104     Note that `MersenneTwisterEngine([123])` will not result in
105     the same initial state as `MersenneTwisterEngine(123)`.
106     +/
107     this()(scope const(UIntType)[] array) @safe pure nothrow @nogc
108     {
109         static if (is(UIntType == uint))
110         {
111             enum UIntType f2 = 1664525u;
112             enum UIntType f3 = 1566083941u;
113         }
114         else static if (is(UIntType == ulong))
115         {
116             enum UIntType f2 = 3935559000370003845uL;
117             enum UIntType f3 = 2862933555777941757uL;
118         }
119         else
120             static assert(0, "init by slice only supported if UIntType is uint or ulong!");
121 
122         data[$-1] = cast(UIntType) (19650218u & max);
123         foreach_reverse (size_t i, ref e; data[0 .. $-1])
124         {
125             e = f * (data[i + 1] ^ (data[i + 1] >> (w - 2))) + cast(UIntType)(n - (i + 1));
126             static if (max != UIntType.max)
127                 e &= max;
128         }
129         index = n-1;
130         if (array.length == 0)
131         {
132             opCall();
133             return;
134         }
135 
136         size_t final_mix_index = void;
137 
138         if (array.length >= n)
139         {
140             size_t j = 0;
141             //Handle all but tail.
142             while (array.length - j >= n - 1)
143             {
144                 foreach_reverse (i, ref e; data[0 .. $-1])
145                 {
146                     e = (e ^ ((data[i+1] ^ (data[i+1] >> (w - 2))) * f2))
147                         + array[j] + cast(UIntType) j;
148                     static if (max != UIntType.max)
149                         e &= max;
150                     ++j;
151                 }
152                 data[$ - 1] = data[0];
153             }
154             //Handle tail.
155             size_t i = n - 2;
156             while (j < array.length)
157             {
158                 data[i] = (data[i] ^ ((data[i+1] ^ (data[i+1] >> (w - 2))) * f2))
159                     + array[j] + cast(UIntType) j;
160                 static if (max != UIntType.max)
161                     data[i] &= max;
162                 ++j;
163                 --i;
164             }
165             //Set the index for use by the next pass.
166             final_mix_index = i;
167         }
168         else
169         {
170             size_t i = n - 2;
171             //Handle all but tail.
172             while (i >= array.length)
173             {
174                 foreach (j; 0 .. array.length)
175                 {
176                     data[i] = (data[i] ^ ((data[i+1] ^ (data[i+1] >> (w - 2))) * f2))
177                         + array[j] + cast(UIntType) j;
178                     static if (max != UIntType.max)
179                         data[i] &= max;
180                     --i;
181                 }
182             }
183             //Handle tail.
184             size_t j = 0;
185             while (i != cast(size_t) -1)
186             {
187                 data[i] = (data[i] ^ ((data[i+1] ^ (data[i+1] >> (w - 2))) * f2))
188                     + array[j] + cast(UIntType) j;
189                 static if (max != UIntType.max)
190                     data[i] &= max;
191                 ++j;
192                 --i;
193             }
194             data[$ - 1] = data[0];
195             i = n - 2;
196             data[i] = (data[i] ^ ((data[i+1] ^ (data[i+1] >> (w - 2))) * f2))
197                 + array[j] + cast(UIntType) j;
198             static if (max != UIntType.max)
199                 data[i] &= max;
200             //Set the index for use by the next pass.
201             final_mix_index = n - 2;
202         }
203 
204         foreach_reverse (i, ref e; data[0 .. final_mix_index])
205         {
206             e = (e ^ ((data[i+1] ^ (data[i+1] >> (w - 2))) * f3))
207                 - cast(UIntType)(n - (i + 1));
208             static if (max != UIntType.max)
209                 e &= max;
210         }
211         foreach_reverse (i, ref e; data[final_mix_index .. n-1])
212         {
213             e = (e ^ ((data[i+1] ^ (data[i+1] >> (w - 2))) * f3))
214                 - cast(UIntType)(n - (i + 1));
215             static if (max != UIntType.max)
216                 e &= max;
217         }
218         data[$-1] = (cast(UIntType)1) << ((UIntType.sizeof * 8) - 1); /* MSB is 1; assuring non-zero initial array */
219         opCall();
220     }
221 
222     /++
223     Advances the generator.
224     +/
225     UIntType opCall() @safe pure nothrow @nogc
226     {
227         // This function blends two nominally independent
228         // processes: (i) calculation of the next random
229         // variate from the cached previous `data` entry
230         // `_z`, and (ii) updating `data[index]` and `_z`
231         // and advancing the `index` value to the next in
232         // sequence.
233         //
234         // By interweaving the steps involved in these
235         // procedures, rather than performing each of
236         // them separately in sequence, the variables
237         // are kept 'hot' in CPU registers, allowing
238         // for significantly faster performance.
239         sizediff_t index = this.index;
240         sizediff_t next = index - 1;
241         if(next < 0)
242             next = n - 1;
243         auto z = _z;
244         sizediff_t conj = index - m;
245         if(conj < 0)
246             conj = index - m + n;
247         static if (d == UIntType.max)
248             z ^= (z >> u);
249         else
250             z ^= (z >> u) & d;
251         auto q = data[index] & upperMask;
252         auto p = data[next] & lowerMask;
253         z ^= (z << s) & b;
254         auto y = q | p;
255         auto x = y >> 1;
256         z ^= (z << t) & c;
257         if (y & 1)
258             x ^= a;
259         auto e = data[conj] ^ x;
260         z ^= (z >> l);
261         _z = data[index] = e;
262         this.index = next;
263         return z;
264     }
265 }
266 
267 /++
268 A $(D MersenneTwisterEngine) instantiated with the parameters of the
269 original engine $(HTTP en.wikipedia.org/wiki/Mersenne_Twister,
270 MT19937), generating uniformly-distributed 32-bit numbers with a
271 period of 2 to the power of 19937.
272 
273 This is recommended for random number generation on 32-bit systems
274 unless memory is severely restricted, in which case a
275 $(REF_ALTTEXT Xorshift, Xorshift, mir, random, engine, xorshift)
276 would be the generator of choice.
277 +/
278 alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31,
279                                        0x9908b0df, 11, 0xffffffff, 7,
280                                        0x9d2c5680, 15,
281                                        0xefc60000, 18, 1812433253);
282 
283 ///
284 @safe version(mir_random_test) unittest
285 {
286     import mir.random.engine;
287 
288     // bit-masking by generator maximum is necessary
289     // to handle 64-bit `unpredictableSeed`
290     auto gen = Mt19937(unpredictableSeed & Mt19937.max);
291     auto n = gen();
292 
293     import std.traits;
294     static assert(is(ReturnType!gen == uint));
295 }
296 
297 /++
298 A $(D MersenneTwisterEngine) instantiated with the parameters of the
299 original engine $(HTTP en.wikipedia.org/wiki/Mersenne_Twister,
300 MT19937), generating uniformly-distributed 64-bit numbers with a
301 period of 2 to the power of 19937.
302 
303 This is recommended for random number generation on 64-bit systems
304 unless memory is severely restricted, in which case a
305 $(REF_ALTTEXT Xorshift, Xorshift, mir, random, engine, xorshift)
306 would be the generator of choice.
307 +/
308 alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31,
309                                           0xb5026f5aa96619e9, 29, 0x5555555555555555, 17,
310                                           0x71d67fffeda60000, 37,
311                                           0xfff7eee000000000, 43, 6364136223846793005);
312 
313 ///
314 @safe version(mir_random_test) unittest
315 {
316     import mir.random.engine;
317 
318     auto gen = Mt19937_64(unpredictableSeed);
319     auto n = gen();
320 
321     import std.traits;
322     static assert(is(ReturnType!gen == ulong));
323 }
324 
325 @safe nothrow version(mir_random_test) unittest
326 {
327     import mir.random.engine;
328 
329     static assert(isSaturatedRandomEngine!Mt19937);
330     static assert(isSaturatedRandomEngine!Mt19937_64);
331     auto gen = Mt19937(Mt19937.defaultSeed);
332     foreach(_; 0 .. 9999)
333         gen();
334     assert(gen() == 4123659995);
335 
336     auto gen64 = Mt19937_64(Mt19937_64.defaultSeed);
337     foreach(_; 0 .. 9999)
338         gen64();
339     assert(gen64() == 9981545732273789042uL);
340 }
341 
342 version(mir_random_test) unittest
343 {
344     enum val = [1341017984, 62051482162767];
345     alias MT(UIntType, uint w) = MersenneTwisterEngine!(UIntType, w, 624, 397, 31,
346                                                         0x9908b0df, 11, 0xffffffff, 7,
347                                                         0x9d2c5680, 15,
348                                                         0xefc60000, 18, 1812433253);
349 
350     import std.meta: AliasSeq;
351     foreach (i, R; AliasSeq!(MT!(ulong, 32), MT!(ulong, 48)))
352     {
353         static if (R.wordSize == 48) static assert(R.max == 0xFFFFFFFFFFFF);
354         auto a = R(R.defaultSeed);
355         foreach(_; 0..999)
356             a();
357         assert(val[i] == a());
358     }
359 }
360 
361 @safe nothrow @nogc version(mir_random_test) unittest
362 {
363     //Verify that seeding with an array gives the same result as the reference
364     //implementation.
365 
366     //32-bit: www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/MT2002/CODES/mt19937ar.tgz
367     immutable uint[4] seed32 = [0x123u, 0x234u, 0x345u, 0x456u];
368     auto gen32 = Mt19937(seed32);
369     foreach(_; 0..999)
370         gen32();
371     assert(3460025646u == gen32());
372 
373     //64-bit: www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/mt19937-64.tgz
374     immutable ulong[4] seed64 = [0x12345uL, 0x23456uL, 0x34567uL, 0x45678uL];
375     auto gen64 = Mt19937_64(seed64);
376     foreach(_; 0..999)
377         gen64();
378     assert(994412663058993407uL == gen64());
379 }