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 }