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