1 /** A D port of the PCG pseudorandom number generator at http://pcg-random.org 2 3 Most of the functionality is tested against the PCG C++ implementation to ensure 4 that the output matches. That is, the D generators should produce the same output as the 5 C++ ones. The exception is default-initialized, unseeded generators, due to D not allowing 6 a default constructor on structs. 7 8 64-bit generators are not supported yet, as they require the (currently unsupported) 9 128-bit integer types that D has not yet implemented. 10 11 Unique generators are also not supported. Seed with std.random.unpredictableSeed instead. 12 13 See: https://www.pcg-random.org/index.html 14 Origin: https://github.com/ColonelThirtyTwo/d-pcg 15 */ 16 module nxt.pcg; 17 18 import std.traits; 19 import std.typetuple; 20 21 version (unittest) { 22 import std.range; 23 import std.algorithm; 24 } 25 26 private { 27 // Type to use for bit counts. 28 alias bits_t = size_t; 29 30 31 // Generator constants. Depends on the integer type. 32 template default_multiplier(T) { static assert(false); } 33 template default_increment(T) { static assert(false); } 34 35 enum ubyte default_multiplier(T : ubyte) = 141U; 36 enum ushort default_multiplier(T : ushort) = 12829U; 37 enum uint default_multiplier(T : uint) = 747796405U; 38 enum ulong default_multiplier(T : ulong) = 6364136223846793005UL; 39 40 enum ubyte default_increment(T : ubyte) = 77U; 41 enum ushort default_increment(T : ushort) = 47989U; 42 enum uint default_increment(T : uint) = 2891336453U; 43 enum ulong default_increment(T : ulong) = 1442695040888963407UL; 44 45 unittest { 46 static assert(default_multiplier!ubyte == 141); 47 static assert(default_multiplier!ushort == 12829); 48 static assert(!__traits(compiles, default_multiplier!string)); 49 } 50 51 template mcg_multiplier(T) { static assert(false); } 52 template mcg_unmultiplier(T) { static assert(false); } 53 54 enum ubyte mcg_multiplier(T : ubyte) = 217U; 55 enum ushort mcg_multiplier(T : ushort) = 62169U; 56 enum uint mcg_multiplier(T : uint) = 277803737U; 57 enum ulong mcg_multiplier(T : ulong) = 12605985483714917081UL; 58 59 enum ubyte mcg_unmultiplier(T : ubyte) = 105U; 60 enum ushort mcg_unmultiplier(T : ushort) = 28009U; 61 enum uint mcg_unmultiplier(T : uint) = 2897767785U; 62 enum ulong mcg_unmultiplier(T : ulong) = 15009553638781119849UL; 63 64 template HalfSize(T) { static assert(false, "no half size for type "~T.stringof); } 65 alias HalfSize(T : ushort) = ubyte; 66 alias HalfSize(T : uint) = ushort; 67 alias HalfSize(T : ulong) = uint; 68 69 T rotr(T)(T val, bits_t shift) 70 if (isUnsigned!T) { 71 /+ TODO: implement in ASM +/ 72 enum bits = T.sizeof*8; 73 enum mask = bits-1; 74 75 return cast(T)(val >> shift) | cast(T)(val << ((-shift) & mask)); 76 } 77 78 unittest { 79 assert(rotr!ubyte(1<<1, 1) == 1); 80 assert(rotr!ubyte(1<<5, 2) == 1<<3); 81 assert(rotr!ubyte(1, 3) == 1<<5); 82 83 assert(rotr!ubyte(3<<6, 2) == 3<<4); 84 assert(rotr!ubyte(0xF0, 4) == 0x0F); 85 } 86 } 87 88 /** Generator variations. 89 90 Unique is not supported; using `this` as an lvalue in D is deprecated. 91 Use std.random.unpredictableSeed instead. 92 */ 93 94 /** MCG Generator. 95 96 No multi-stream support. Slightly faster, but smaller period. 97 */ 98 mixin template MCG(StateType) { 99 enum VariationName = "MCG"; 100 enum ulong Period_pow2 = StateType.sizeof*8 - 2; 101 enum ulong MaxStream = 0; 102 enum stream = 0; 103 private enum StateType increment = 0; 104 } 105 106 /** OneSeq Generator. 107 108 No multi-stream support. 109 */ 110 mixin template OneSeq(StateType) { 111 enum VariationName = "OneSeq"; 112 enum ulong Period_pow2 = StateType.sizeof*8; 113 enum ulong MaxStream = 0; 114 enum stream = 0; 115 private enum StateType increment = default_increment!StateType; 116 } 117 118 /** SetSeq Generator. 119 120 Has multi-stream support. 121 */ 122 mixin template SetSeq(StateType) { 123 enum VariationName = "SetSeq"; 124 enum ulong Period_pow2 = StateType.sizeof*8; 125 enum ulong MaxStream = StateType.max >> 1; 126 127 private StateType stream_inc = default_increment!StateType; 128 129 private StateType increment() const pure nothrow @nogc @property { 130 return stream_inc; 131 } 132 StateType stream(StateType seq) pure nothrow @nogc @property { 133 return stream_inc = cast(StateType) (seq << 1) | 1; 134 } 135 StateType stream() const pure nothrow @nogc @property { 136 return stream_inc >> 1; 137 } 138 } 139 140 unittest { 141 void CheckVariation(alias Variation, T)() { 142 static struct Foo { 143 mixin Variation!(T); 144 } 145 } 146 147 CheckVariation!(MCG, ubyte); 148 CheckVariation!(MCG, ushort); 149 CheckVariation!(MCG, uint); 150 CheckVariation!(MCG, ulong); 151 152 CheckVariation!(OneSeq, ubyte); 153 CheckVariation!(OneSeq, ushort); 154 CheckVariation!(OneSeq, uint); 155 CheckVariation!(OneSeq, ulong); 156 157 CheckVariation!(SetSeq, ubyte); 158 CheckVariation!(SetSeq, ushort); 159 CheckVariation!(SetSeq, uint); 160 CheckVariation!(SetSeq, ulong); 161 } 162 163 /** The main PCG engine. */ 164 struct PCGEngine( 165 ResultType, 166 StateType, 167 alias OutputFunc, 168 bool OutputPrevious=true, 169 alias StreamTypeMixin=OneSeq, 170 StateType Multiplier=default_multiplier!StateType 171 ) { 172 static assert(isUnsigned!ResultType, "ResultType must be an unsigned integer, not "~ResultType.stringof); 173 static assert(isUnsigned!StateType, "StateType must be an unsigned integer, not "~StateType.stringof); 174 static assert(is(typeof(OutputFunc!(StateType, ResultType))), "Invalid OutputFunc used with PCGEngine"); 175 176 mixin StreamTypeMixin!StateType; 177 178 public: 179 enum isUniformRandom = true; 180 enum min = ResultType.min; 181 enum max = ResultType.max; 182 enum empty = false; 183 184 /** Creates and seeds a new RNG. */ 185 this(StateType seed) pure { 186 this.seed(seed); 187 } 188 189 static if (MaxStream > 0) { 190 /** Creates and seeds a new RNG, at the specified stream. 191 Only available if using the SetSeq variation. 192 */ 193 this(StateType seed, StateType stream) pure { 194 this.seed(seed, stream); 195 } 196 } 197 198 /// 199 ResultType front() @property const pure nothrow @nogc { 200 return _front; 201 } 202 203 /// 204 void popFront() pure nothrow @nogc { 205 _front = OutputFunc!(StateType, ResultType)(base_generate()); 206 } 207 208 /// 209 inout(typeof(this)) save() inout pure nothrow @nogc { 210 return this; 211 } 212 213 private void advance(StateType amount) { 214 StateType accMult = 1; 215 StateType accPlus = 0; 216 auto curMult = Multiplier; 217 auto curPlus = increment; 218 219 while (amount > 0) { 220 if (amount & 1) { 221 accMult *= curMult; 222 accPlus = accPlus*curMult + curPlus; 223 } 224 curPlus = (curMult+1)*curPlus; 225 curMult *= curMult; 226 amount >>= 1; 227 } 228 state = accMult * state + accPlus; 229 } 230 231 /// Advances the RNG faster than calling popFrontN multiple times. 232 void popFrontN(StateType amount) { 233 if (amount == 0) 234 return; 235 advance(amount-1); 236 popFront(); 237 } 238 239 /// Seeds the RNG. 240 void seed(StateType seed) pure nothrow @nogc { 241 static if (VariationName == "MCG") 242 state = seed | 3; 243 else 244 state = bump(seed + increment); 245 this.popFront(); 246 } 247 248 static if (MaxStream > 0) { 249 /** Seeds the RNG and sets the stream. 250 * Only available if using the SetSeq variation. 251 */ 252 void seed(StateType seed, StateType stream) { 253 this.stream = stream; 254 this.seed(seed); 255 } 256 } 257 258 private: 259 StateType state = cast(StateType) 0xcafef00dd15ea5e5UL; 260 ResultType _front; 261 262 StateType bump(StateType state) const pure nothrow @nogc { 263 return state * Multiplier + increment; 264 } 265 266 StateType base_generate() pure nothrow @nogc { 267 static if (OutputPrevious) { 268 StateType oldState = state; 269 state = bump(state); 270 return oldState; 271 } else { 272 return state = bump(state); 273 } 274 } 275 } 276 277 /* Pre-defined PCG generators, matching the original source code's definitions. 278 279 PCG32 support multiple streams, PCG32OneSeq and PCG32Fast do not. PCG32Fast is slightly faster, but has a shorter period. 280 */ 281 alias PCG32 = PCGEngine!(uint, ulong, xsh_rr, true, SetSeq); 282 /// ditto 283 alias PCG32OneSeq = PCGEngine!(uint, ulong, xsh_rr, true, OneSeq); 284 /// ditto 285 alias PCG32Fast = PCGEngine!(uint, ulong, xsh_rs, true, MCG); 286 287 unittest { 288 import std.random; 289 static assert(isUniformRNG!(PCG32, uint)); 290 static assert(isSeedable!(PCG32, ulong)); 291 } 292 unittest { 293 auto gen = PCG32(12345); 294 immutable testVector = [1411482639,3165192603,3360792183,2433038347,628889468,3778631550,2430531221,2504758782,1116223725,3013600377,]; 295 assert(gen.take(testVector.length).equal(testVector)); 296 } 297 unittest { 298 auto gen = PCG32(12345, 678); 299 immutable testVector = [3778649606,938063991,2368796250,1689035216,3455663708,3248344731,1831696548,608339070,2791141168,1236841111,]; 300 assert(gen.take(testVector.length).equal(testVector)); 301 } 302 unittest { 303 auto gen = PCG32OneSeq(12345); 304 immutable testVector = [1411482639,3165192603,3360792183,2433038347,628889468,3778631550,2430531221,2504758782,1116223725,3013600377,]; 305 assert(gen.take(testVector.length).equal(testVector)); 306 } 307 unittest { 308 auto gen = PCG32Fast(12345); 309 immutable testVector = [0,360236480,3576984158,3399133164,3953016716,1075407150,134550159,1121230050,697817023,1550232051,]; 310 assert(gen.take(testVector.length).equal(testVector)); 311 } 312 unittest { 313 auto gen1 = PCG32(456789); 314 auto gen2 = gen1.save; 315 316 enum iters = 10000; 317 gen1.popFrontN(10000); 318 foreach(i; 0..10000) 319 gen2.popFront(); 320 assert(gen1.take(10).equal(gen2.take(10))); 321 } 322 323 /* Output functions. 324 325 Each function has a unit test that compares it to some hardcoded 326 randomly-generated values from the C++ versions of the function, to ensure 327 that they work the same way. 328 */ 329 330 private template BitInfo(StateType, ResultType) { 331 static assert(isUnsigned!StateType, "Invalid state type"); 332 static assert(isUnsigned!ResultType, "Invalid result type"); 333 static assert(StateType.sizeof >= ResultType.sizeof, "Cannot have a state type larger than the result type."); 334 335 enum stateBits = StateType.sizeof * 8; 336 enum resultBits = ResultType.sizeof * 8; 337 enum spareBits = stateBits - resultBits; 338 } 339 340 /** Output function XSH RS -- high xorshift, followed by a random shift 341 342 Fast. A good performer. 343 */ 344 ResultType xsh_rs(StateType, ResultType)(StateType state) { 345 alias bi = BitInfo!(StateType, ResultType); 346 enum opBits = bi.spareBits-5 >= 64 ? 5 347 : bi.spareBits-4 >= 32 ? 4 348 : bi.spareBits-3 >= 16 ? 3 349 : bi.spareBits-2 >= 4 ? 2 350 : bi.spareBits-1 >= 1 ? 1 351 : 0; 352 enum mask = (1 << opBits) - 1; 353 enum maxRandShift = mask; 354 enum topSpare = opBits; 355 enum bottomSpare = bi.spareBits - topSpare; 356 enum xShift = topSpare + (bi.resultBits + maxRandShift) / 2; 357 358 bits_t rshift = opBits ? (state >> (bi.stateBits - opBits)) & mask : 0; 359 state ^= state >> xShift; 360 return cast(ResultType)(state >> (bottomSpare - maxRandShift + rshift)); 361 } 362 363 unittest { 364 auto v = xsh_rs!(ulong, uint)(1234UL); 365 static assert(is(typeof(v) == uint)); 366 static assert(!__traits(compiles, xsh_rs!(uint, ulong)(123))); 367 368 immutable testVectors = [ 369 [15067669103579037956UL,296871741UL], 370 [10585216671734060556UL,3113159775UL], 371 [14465915293962989487UL,2350131006UL], 372 [12560359203105191607UL,3387675550UL], 373 [16381757648301003838UL,448631329UL], 374 [18405880340653979308UL,4218850008UL], 375 [15777501264337822631UL,2941200983UL], 376 [12749535132123028338UL,502140052UL], 377 [15452512430144842739UL,1730516846UL], 378 [15214490214938895492UL,843814672UL], 379 ]; 380 foreach(vec; testVectors) { 381 assert(xsh_rs!(ulong, uint)(vec[0]) == vec[1]); 382 } 383 } 384 385 /** Output function XSH RR -- high xorshift, followed by a random rotate 386 387 Fast. A good performer. Slightly better statistically than XSH RS. 388 */ 389 ResultType xsh_rr(StateType, ResultType)(StateType state) { 390 alias bi = BitInfo!(StateType, ResultType); 391 enum wantedOpBits = bi.resultBits >= 128 ? 7 392 : bi.resultBits >= 64 ? 6 393 : bi.resultBits >= 32 ? 5 394 : bi.resultBits >= 16 ? 4 395 : 3; 396 enum opBits = bi.spareBits > wantedOpBits ? wantedOpBits : bi.spareBits; 397 enum amplifier = wantedOpBits - opBits; 398 enum mask = (1 << opBits) - 1; 399 enum topSpare = opBits; 400 enum bottomSpare = bi.spareBits - topSpare; 401 enum xShift = (topSpare + bi.resultBits) / 2; 402 403 bits_t rot = opBits ? cast(bits_t)(state >> (bi.stateBits - opBits)) & mask : 0; 404 bits_t ampRot = cast(bits_t)(rot << amplifier) & mask; 405 state ^= state >> xShift; 406 return rotr(cast(ResultType)(state >> bottomSpare), ampRot); 407 } 408 409 unittest { 410 auto v = xsh_rr!(ulong, uint)(1234UL); 411 static assert(is(typeof(v) == uint)); 412 static assert(!__traits(compiles, xsh_rr!(uint, ulong)(123))); 413 414 immutable testVectors = [ 415 [15067669103579037956UL,3645606856UL], 416 [10585216671734060556UL,3592116016UL], 417 [14465915293962989487UL,389417868UL], 418 [12560359203105191607UL,2008424015UL], 419 [16381757648301003838UL,2937207046UL], 420 [18405880340653979308UL,3686489923UL], 421 [15777501264337822631UL,3541945963UL], 422 [12749535132123028338UL,2941149303UL], 423 [15452512430144842739UL,2474483187UL], 424 [15214490214938895492UL,611820953UL], 425 ]; 426 foreach(vec; testVectors) { 427 assert(xsh_rr!(ulong, uint)(vec[0]) == vec[1]); 428 } 429 } 430 431 /** Output function RXS -- random xorshift */ 432 ResultType rxs(StateType, ResultType)(StateType state) { 433 alias bi = BitInfo!(StateType, ResultType); 434 enum shift = bi.stateBits - bi.resultBits; 435 enum extraShift = (bi.resultBits - shift) / 2; 436 bits_t rshift = shift > 64+8 ? (state >> (bi.stateBits - 6)) & 63 437 : shift > 32+4 ? (state >> (bi.stateBits - 5)) & 31 438 : shift > 16+2 ? (state >> (bi.stateBits - 4)) & 15 439 : shift > 8+1 ? (state >> (bi.stateBits - 3)) & 7 440 : shift > 4+1 ? (state >> (bi.stateBits - 2)) & 3 441 : shift > 2+1 ? (state >> (bi.stateBits - 1)) & 1 442 : 0; 443 state ^= state >> (shift + extraShift - rshift); 444 return cast(ResultType) (state >> rshift); 445 } 446 447 unittest { 448 auto v = rxs!(ulong, uint)(1234UL); 449 static assert(is(typeof(v) == uint)); 450 static assert(!__traits(compiles, rxs!(uint, ulong)(123))); 451 452 immutable testVectors = [ 453 [15067669103579037956UL,950461087UL], 454 [10585216671734060556UL,2945752657UL], 455 [14465915293962989487UL,3721378190UL], 456 [12560359203105191607UL,2805810440UL], 457 [16381757648301003838UL,611454941UL], 458 [18405880340653979308UL,1512285355UL], 459 [15777501264337822631UL,1417801921UL], 460 [12749535132123028338UL,3826008940UL], 461 [15452512430144842739UL,118172308UL], 462 [15214490214938895492UL,46684810UL], 463 ]; 464 foreach(vec; testVectors) { 465 assert(rxs!(ulong, uint)(vec[0]) == vec[1]); 466 } 467 } 468 469 /** Output function RXS M XS -- random xorshift, mcg multiply, fixed xorshift 470 471 The most statistically powerful generator, but all those steps 472 make it slower than some of the others. We give it the rottenest jobs. 473 474 Because it's usually used in contexts where the state type and the 475 result type are the same, it is a permutation and is thus invertable. 476 We thus provide a function to invert it. This function is used to 477 for the "inside out" generator used by the extended generator. 478 */ 479 ResultType rxs_m_xs(StateType, ResultType)(StateType state) { 480 alias bi = BitInfo!(StateType, ResultType); 481 enum opBits = bi.resultBits >= 128 ? 6 482 : bi.resultBits >= 64 ? 5 483 : bi.resultBits >= 32 ? 4 484 : bi.resultBits >= 16 ? 3 485 : 2; 486 enum shift = bi.stateBits - bi.resultBits; 487 enum mask = cast(bits_t) (1 << opBits) - 1; 488 489 bits_t rshift = opBits ? cast(bits_t)(state >> (bi.stateBits - opBits)) & mask : 0; 490 state ^= state >> (opBits + rshift); 491 state *= mcg_multiplier!StateType; 492 ResultType result = cast(ResultType)(state >> shift); 493 result ^= cast(ResultType)(result >> ((2*bi.resultBits+2)/3)); 494 return result; 495 } 496 497 unittest { 498 auto v = rxs_m_xs!(ulong, uint)(1234UL); 499 static assert(is(typeof(v) == uint)); 500 static assert(!__traits(compiles, rxs_m_xs!(uint, ulong)(123))); 501 502 immutable testVectors = [ 503 [15067669103579037956UL,3319069165UL], 504 [10585216671734060556UL,357161392UL], 505 [14465915293962989487UL,614575156UL], 506 [12560359203105191607UL,934180987UL], 507 [16381757648301003838UL,490692769UL], 508 [18405880340653979308UL,4215630753UL], 509 [15777501264337822631UL,575635089UL], 510 [12749535132123028338UL,1920410296UL], 511 [15452512430144842739UL,2192536769UL], 512 [15214490214938895492UL,2986597130UL], 513 ]; 514 foreach(vec; testVectors) { 515 assert(rxs_m_xs!(ulong, uint)(vec[0]) == vec[1]); 516 } 517 } 518 519 /** Output function RXS M -- random xorshift, mcg multiply. */ 520 ResultType rxs_m(StateType, ResultType)(StateType state) { 521 alias bi = BitInfo!(StateType, ResultType); 522 enum opBits = bi.resultBits >= 128 ? 6 523 : bi.resultBits >= 64 ? 5 524 : bi.resultBits >= 32 ? 4 525 : bi.resultBits >= 16 ? 3 526 : 2; 527 enum shift = bi.stateBits - bi.resultBits; 528 enum mask = (1 << opBits) - 1; 529 bits_t rShift = opBits ? (state >> (bi.stateBits - opBits)) & mask : 0; 530 state ^= state >> (opBits + rShift); 531 state *= mcg_multiplier!StateType; 532 return state >> shift; 533 } 534 535 unittest { 536 auto v = rxs_m!(ulong, uint)(1234UL); 537 static assert(is(typeof(v) == uint)); 538 static assert(!__traits(compiles, rxs_m!(uint, ulong)(123))); 539 540 immutable testVectors = [ 541 [15067669103579037956UL,3319069434UL], 542 [10585216671734060556UL,357161445UL], 543 [14465915293962989487UL,614575270UL], 544 [12560359203105191607UL,934181029UL], 545 [16381757648301003838UL,490692821UL], 546 [18405880340653979308UL,4215629900UL], 547 [15777501264337822631UL,575634968UL], 548 [12749535132123028338UL,1920410481UL], 549 [15452512430144842739UL,2192537291UL], 550 [15214490214938895492UL,2986596802UL], 551 ]; 552 foreach(vec; testVectors) { 553 assert(rxs_m!(ulong, uint)(vec[0]) == vec[1]); 554 } 555 } 556 557 /** Output function XSL RR -- fixed xorshift (to low bits), random rotate 558 559 Useful for 128-bit types that are split across two CPU registers. 560 */ 561 ResultType xsl_rr(StateType, ResultType)(StateType state) { 562 alias bi = BitInfo!(StateType, ResultType); 563 enum spareBits = bi.stateBits - bi.resultBits; 564 enum wantedOpBits = bi.resultBits >= 128 ? 7 565 : bi.resultBits >= 64 ? 6 566 : bi.resultBits >= 32 ? 5 567 : bi.resultBits >= 16 ? 4 568 : 3; 569 enum opBits = spareBits > wantedOpBits ? wantedOpBits : spareBits; 570 enum amplifier = wantedOpBits - opBits; 571 enum mask = (1 << opBits) - 1; 572 enum topSpare = spareBits; 573 enum bottomSpare = spareBits - topSpare; 574 enum xShift = (topSpare + bi.resultBits) / 2; 575 576 bits_t rot = opBits ? cast(bits_t)(state >> (bi.stateBits - opBits)) & mask : 0; 577 bits_t ampRot = (rot << amplifier) & mask; 578 state ^= state >> xShift; 579 return rotr(cast(ResultType)(state >> bottomSpare), ampRot); 580 } 581 582 unittest { 583 auto v = xsl_rr!(ulong, uint)(1234UL); 584 static assert(is(typeof(v) == uint)); 585 static assert(!__traits(compiles, xsl_rr!(uint, ulong)(123))); 586 587 immutable testVectors = [ 588 [15067669103579037956UL,3146190043UL], 589 [10585216671734060556UL,2585632233UL], 590 [14465915293962989487UL,2790752147UL], 591 [12560359203105191607UL,1599378225UL], 592 [16381757648301003838UL,3442106232UL], 593 [18405880340653979308UL,2295384084UL], 594 [15777501264337822631UL,1520586031UL], 595 [12749535132123028338UL,2225559205UL], 596 [15452512430144842739UL,424441662UL], 597 [15214490214938895492UL,3432492627UL], 598 ]; 599 foreach(vec; testVectors) { 600 assert(xsl_rr!(ulong, uint)(vec[0]) == vec[1]); 601 } 602 } 603 604 // todo: xsl_rr_rr_mixin, xsh_mixin, xsl_mixin