1 /++ 2 + Permuted Congruential Generator (PCG) 3 + 4 + Implemented as per the C++ version of PCG, $(HTTP _pcg-random.org). 5 + 6 + Paper available $(HTTP _pcg-random.org/paper.html) 7 + 8 + Author: Melissa O'Neill (C++). D translation Nicholas Wilson. 9 + 10 + PCG Random Number Generation for C++ 11 + 12 + Copyright 2014 Melissa O'Neill <oneill@pcg-random.org> 13 + 14 + Licensed under the Apache License, Version 2.0 (the "License"); 15 + you may not use this file except in compliance with the License. 16 + You may obtain a copy of the License at 17 + 18 + $(HTTP www.apache.org/licenses/LICENSE-2.0) 19 + 20 + Unless required by applicable law or agreed to in writing, software 21 + distributed under the License is distributed on an "AS IS" BASIS, 22 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 + See the License for the specific language governing permissions and 24 + limitations under the License. 25 + 26 + For additional information about the PCG random number generation scheme, 27 + including its license and other licensing options, visit 28 + 29 + $(HTTP _pcg-random.org) 30 +/ 31 module mir.random.engine.pcg; 32 33 import mir.random.engine; 34 import std.traits : ReturnType, TemplateArgsOf; 35 36 @safe: 37 nothrow: 38 @nogc: 39 40 /// Select the above mixin templates. 41 enum stream_t 42 { 43 /// Increment is cast(size_t) &RNG. 44 unique, 45 /// Increment is 0. 46 none, 47 /// Increment is the default increment. 48 oneseq, 49 /// Increment is runtime setable and defaults to the default (same as oneseq) 50 specific, 51 } 52 53 /// 32-bit output PCGs with 64 bits of state. 54 alias pcg32 = PermutedCongruentialEngine!(xsh_rr!(uint,ulong),stream_t.specific,true); 55 /// ditto 56 alias pcg32_unique = PermutedCongruentialEngine!(xsh_rr!(uint,ulong),stream_t.unique,true); 57 /// ditto 58 alias pcg32_oneseq = PermutedCongruentialEngine!(xsh_rr!(uint,ulong),stream_t.oneseq,true); 59 /// ditto 60 alias pcg32_fast = PermutedCongruentialEngine!(xsh_rr!(uint,ulong),stream_t.none,true); 61 62 static if (__traits(compiles, ucent.max)) 63 { 64 /// 64-bit output PCGs with 128 bits of state. Requires `ucent` type. 65 alias pcg64 = PermutedCongruentialEngine!(xsh_rr!(ulong,ucent),stream_t.specific,true); 66 /// 67 alias pcg64_unique = PermutedCongruentialEngine!(xsh_rr!(ulong,ucent),stream_t.unique,true); 68 /// 69 alias pcg64_oneseq = PermutedCongruentialEngine!(xsh_rr!(ulong,ucent),stream_t.oneseq,true); 70 /// 71 alias pcg64_fast = PermutedCongruentialEngine!(xsh_rr!(ulong,ucent),stream_t.none,true); 72 } 73 74 /// PCGs with n bits output and n bits of state. 75 alias pcg8_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(ubyte ,ubyte ),stream_t.specific,true); 76 /// ditto 77 alias pcg16_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(ushort,ushort),stream_t.specific,true); 78 /// ditto 79 alias pcg32_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(uint ,uint ),stream_t.specific,true); 80 /// ditto 81 alias pcg64_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(ulong,ulong ),stream_t.specific,true); 82 //alias pcg128_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(ucent,ucent,stream_t.specific,true); 83 84 /// As above but the increment is not dynamically setable. 85 alias pcg8_oneseq_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(ubyte ,ubyte ),stream_t.oneseq,true); 86 /// ditto 87 alias pcg16_oneseq_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(ushort,ushort),stream_t.oneseq,true); 88 /// ditto 89 alias pcg32_oneseq_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(uint ,uint ),stream_t.oneseq,true); 90 /// ditto 91 alias pcg64_oneseq_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(ulong,ulong ),stream_t.oneseq,true); 92 /// ditto 93 /// Requires `ucent` type. 94 static if (__traits(compiles, ucent.max)) 95 alias pcg128_oneseq_once_insecure = PermutedCongruentialEngine!(rxs_m_xs_forward!(ucent,ucent ),stream_t.specific,true); 96 97 /++ 98 + The PermutedCongruentialEngine: 99 + Params: 100 + output = should be one of the above functions. 101 + Controls the output permutation of the state. 102 + streamType = one of unique, none, oneseq, specific. 103 + Controls the Increment of the LCG portion of the PCG. 104 + output_previous = if true then the pre-advance version (increasing instruction-level parallelism) 105 + if false then use the post-advance version (reducing register pressure) 106 + mult_ = optionally set the multiplier for the LCG. 107 +/ 108 struct PermutedCongruentialEngine(alias output, // Output function 109 stream_t streamType, // The stream type 110 bool output_previous, 111 mult_...) if (mult_.length <= 1) 112 { 113 /// 114 enum isRandomEngine = true; 115 116 /// 117 alias Uint = TemplateArgsOf!output[1]; 118 119 static if (mult_.length == 0) 120 enum mult = default_multiplier!Uint; 121 else 122 { 123 static assert(is(typeof(mult_[0]) == Uint), 124 "The specified multiplier must be the state type of the output function"); 125 enum mult = mult_[0]; 126 } 127 128 @disable this(this); 129 @disable this(); 130 static if (streamType == stream_t.none) 131 mixin no_stream!Uint; 132 else static if (streamType == stream_t.unique) 133 mixin unique_stream!Uint; 134 else static if (streamType == stream_t.specific) 135 mixin specific_stream!Uint; 136 else static if (streamType == stream_t.oneseq) 137 mixin oneseq_stream!Uint; 138 else 139 static assert(0); 140 141 /// 142 Uint state; 143 144 /// 145 enum period_pow2 = Uint.sizeof*8 - 2*is_mcg; 146 147 /// 148 enum max = (ReturnType!output).max; 149 150 private: 151 152 static if (__traits(compiles, { enum e = mult + increment; })) 153 { 154 static Uint bump()(Uint state_) 155 { 156 return cast(Uint)(state_ * mult + increment); 157 } 158 } 159 else 160 { 161 Uint bump()(Uint state_) 162 { 163 return cast(Uint)(state_ * mult + increment); 164 } 165 } 166 167 Uint base_generate()() 168 { 169 return state = bump(state); 170 } 171 172 Uint base_generate0()() 173 { 174 Uint old_state = state; 175 state = bump(state); 176 return old_state; 177 } 178 179 public: 180 static if (can_specify_stream) 181 /// 182 this()(Uint seed, Uint stream_ = default_increment_unset_stream!Uint) 183 { 184 state = bump(cast(Uint)(seed + increment)); 185 set_stream(stream_); 186 } 187 else 188 /// 189 this()(Uint seed) 190 { 191 static if (is_mcg) 192 state = seed | 3u; 193 else 194 state = bump(cast(Uint)(seed + increment)); 195 } 196 197 /// 198 ReturnType!output opCall()() 199 { 200 static if(output_previous) 201 return output(base_generate0()); 202 else 203 return output(base_generate()); 204 } 205 206 /++ 207 Skip forward in the random sequence in $(BIGOH log n) time. 208 Even though delta is an unsigned integer, we can pass a 209 signed integer to go backwards, it just goes "the long way round". 210 +/ 211 void skip()(Uint delta) 212 { 213 // The method used here is based on Brown, "Random Number Generation 214 // with Arbitrary Stride,", Transactions of the American Nuclear 215 // Society (Nov. 1994). The algorithm is very similar to fast 216 // exponentiation. 217 // 218 // Even though delta is an unsigned integer, we can pass a 219 // signed integer to go backwards, it just goes "the long way round". 220 221 Uint acc_mult = 1, acc_plus = 0; 222 Uint cur_plus = increment, cur_mult = mult; 223 while (delta > 0) 224 { 225 if (delta & 1) 226 { 227 acc_mult *= cur_mult; 228 acc_plus = cast(Uint)(acc_plus * cur_mult + cur_plus); 229 } 230 cur_plus *= cur_mult + 1; 231 cur_mult *= cur_mult; 232 delta >>= 1; 233 } 234 state = cast(Uint)(acc_mult*state + acc_plus); 235 } 236 237 static if (output_previous) 238 { 239 /++ 240 Compatibility with $(LINK2 https://dlang.org/phobos/std_random.html#.isUniformRNG, 241 Phobos library methods). Presents this RNG as an InputRange. 242 Only available if `output_previous == true`. 243 244 The reason that this is enabled when `output_previous == true` is because 245 `front` can be implemented without additional cost. 246 247 `save` is implemented if `streamType` is not [stream_t.unique]. 248 249 This struct disables its default copy constructor and so will only work with 250 Phobos functions that "do the right thing" and take RNGs by reference and 251 do not accidentally make implicit copies. 252 +/ 253 enum bool isUniformRandom = true; 254 /// ditto 255 enum ReturnType!output min = (ReturnType!output).min; 256 /// ditto 257 enum bool empty = false; 258 /// ditto 259 @property ReturnType!output front()() const { return output(state); } 260 /// ditto 261 void popFront()() { state = bump(state); } 262 /// ditto 263 void seed()(Uint seed) { this.__ctor(seed); } 264 /// ditto 265 static if (streamType != stream_t.unique) 266 @property typeof(this) save()() const 267 { 268 typeof(return) result = void; 269 foreach (i, e; this.tupleof) 270 result.tupleof[i] = e; 271 return result; 272 } 273 } 274 } 275 276 @nogc nothrow pure @safe version(mir_random_test) unittest 277 { 278 //Test that the default generators (all having output_previous==true) 279 //can be used as Phobos-style randoms. 280 import std.meta: AliasSeq; 281 import std.random: isSeedable, isPhobosUniformRNG = isUniformRNG; 282 import std.range.primitives: isForwardRange; 283 foreach(RNG; AliasSeq!(pcg32, pcg32_oneseq, pcg32_fast, 284 pcg32_once_insecure, pcg32_oneseq_once_insecure, 285 pcg64_once_insecure, pcg64_oneseq_once_insecure)) 286 { 287 static assert(isPhobosUniformRNG!(RNG, typeof(RNG.max))); 288 static assert(isSeedable!(RNG, RNG.Uint)); 289 static assert(isForwardRange!RNG); 290 auto gen1 = RNG(1); 291 auto gen2 = RNG(2); 292 auto gen3 = gen1.save; 293 gen2.seed(1); 294 assert(gen1 == gen2); 295 immutable a = gen1.front; 296 gen1.popFront(); 297 assert(a == gen2()); 298 assert(gen1.front == gen2()); 299 assert(a == gen3()); 300 assert(gen1.front == gen3()); 301 } 302 303 foreach(RNG; AliasSeq!(pcg32_unique)) 304 { 305 static assert(isPhobosUniformRNG!(RNG, typeof(RNG.max))); 306 static assert(isSeedable!(RNG, RNG.Uint)); 307 } 308 } 309 310 // Default multiplier to use for the LCG portion of the PCG 311 private template default_multiplier(Uint) 312 { 313 static if (is(Uint == ubyte)) 314 enum ubyte default_multiplier = 141u; 315 else static if (is(Uint == ushort)) 316 enum ushort default_multiplier = 12829u; 317 else static if (is(Uint == uint)) 318 enum uint default_multiplier = 747796405u; 319 else static if (is(Uint == ulong)) 320 enum ulong default_multiplier = 6364136223846793005u; 321 else static if (is(ucent) && is(Uint == ucent)) 322 mixin("enum ucent default_multiplier = 0x2360ED051FC65DA44385DF649FCCF645;"); 323 else 324 static assert(0); 325 } 326 327 // Default increment to use for the LCG portion of the PCG 328 private template default_increment(Uint) 329 { 330 static if (is(Uint == ubyte)) 331 enum ubyte default_increment = 77u; 332 else static if (is(Uint == ushort)) 333 enum ushort default_increment = 47989u; 334 else static if (is(Uint == uint)) 335 enum uint default_increment = 2891336453u; 336 else static if (is(Uint == ulong)) 337 enum ulong default_increment = 1442695040888963407u; 338 else static if (is(ucent) && is(Uint == ucent)) 339 mixin("enum ucent default_increment = 0x5851F42D4C957F2D14057B7EF767814F;"); 340 else 341 static assert(0); 342 } 343 344 private template mcg_multiplier(Uint) 345 { 346 static if (is(Uint == ubyte)) 347 enum ubyte mcg_multiplier = 217u; 348 else static if (is(Uint == ushort)) 349 enum ushort mcg_multiplier = 62169u; 350 else static if (is(Uint == uint)) 351 enum uint mcg_multiplier = 277803737u; 352 else static if (is(Uint == ulong)) 353 enum ulong mcg_multiplier = 12605985483714917081u; 354 else static if (is(ucent) && is(Uint == ucent)) 355 mixin("enum ucent mcg_multiplier = 0x6BC8F622C397699CAEF17502108EF2D9;"); 356 else 357 static assert(0); 358 } 359 360 private template mcg_unmultiplier(Uint) 361 { 362 static if (is(Uint == ubyte)) 363 enum ubyte mcg_unmultiplier = 105u; 364 else static if (is(Uint == ushort)) 365 enum ushort mcg_unmultiplier = 28009u; 366 else static if (is(Uint == uint)) 367 enum uint mcg_unmultiplier = 2897767785u; 368 else static if (is(Uint == ulong)) 369 enum ulong mcg_unmultiplier = 15009553638781119849u; 370 else static if (is(ucent) && is(Uint == ucent)) 371 mixin("enum ucent mcg_unmultiplier = 0xC827645E182BC965D04CA582ACB86D69;"); 372 else 373 static assert(0); 374 } 375 376 private template default_increment_unset_stream(Uint) 377 { 378 enum default_increment_unset_stream = (default_increment!Uint & ~1) >> 1; 379 } 380 381 /// Increment for LCG portion of the PCG is the address of the RNG 382 mixin template unique_stream(Uint) 383 { 384 /// 385 enum is_mcg = false; 386 /// 387 @property Uint increment()() const @trusted 388 { 389 return cast(Uint) (&this) | 1; 390 } 391 /// 392 Uint stream()() 393 { 394 return increment >> 1; 395 } 396 /// 397 enum can_specify_stream = false; 398 /// 399 enum size_t streams_pow2 = Uint.sizeof < size_t.sizeof ? Uint.sizeof : size_t.sizeof - 1u; 400 } 401 402 @nogc nothrow pure @system unittest 403 { 404 pcg32_unique gen = pcg32_unique(1); 405 void* address = &gen; 406 assert(gen.increment == (1 | cast(ulong) address)); 407 } 408 409 410 /// Increment is 0. The LCG portion of the PCG is an MCG. 411 mixin template no_stream(Uint) 412 { 413 /// 414 enum is_mcg = true; 415 /// 416 enum Uint increment = 0; 417 418 /// 419 enum can_specify_stream = false; 420 /// 421 enum size_t streams_pow2 = 0; 422 } 423 424 /// Increment of the LCG portion of the PCG is default_increment. 425 mixin template oneseq_stream(Uint) 426 { 427 /// 428 enum is_mcg = false; 429 /// 430 enum Uint increment = default_increment!Uint; 431 /// 432 enum can_specify_stream = false; 433 /// 434 enum size_t streams_pow2 = 0; 435 } 436 437 /// The increment is dynamically settable and defaults to default_increment!T. 438 mixin template specific_stream(Uint) 439 { 440 /// 441 enum is_mcg = false; 442 /// 443 Uint inc_ = default_increment!Uint; 444 /// 445 alias increment = inc_; 446 /// 447 enum can_specify_stream = true; 448 /// 449 void set_stream()(Uint u) 450 { 451 inc_ = cast(Uint)((u << 1) | 1); 452 } 453 /// 454 enum size_t streams_pow2 = size_t.sizeof*8 -1u; 455 } 456 457 /++ 458 + XorShifts are invertible, but they are someting of a pain to invert. 459 + This function backs them out. It's used by the whacky "inside out" 460 + generator defined later. 461 +/ 462 Uint unxorshift(Uint)(Uint x, size_t bits, size_t shift) 463 { 464 if (2*shift >= bits) { 465 return x ^ (x >> shift); 466 } 467 Uint lowmask1 = (itype(1U) << (bits - shift*2)) - 1; 468 Uint highmask1 = ~lowmask1; 469 Uint top1 = x; 470 Uint bottom1 = x & lowmask1; 471 top1 ^= top1 >> shift; 472 top1 &= highmask1; 473 x = top1 | bottom1; 474 Uint lowmask2 = (itype(1U) << (bits - shift)) - 1; 475 Uint bottom2 = x & lowmask2; 476 bottom2 = unxorshift(bottom2, bits - shift, shift); 477 bottom2 &= lowmask1; 478 return top1 | bottom2; 479 } 480 481 482 /++ 483 + OUTPUT FUNCTIONS. 484 + 485 + These are the core of the PCG generation scheme. They specify how to 486 + turn the base LCG's internal state into the output value of the final 487 + generator. 488 + 489 + All of the classes have code that is written to allow it to be applied 490 + at *arbitrary* bit sizes. 491 +/ 492 493 /++ 494 + XSH RS -- high xorshift, followed by a random shift 495 + 496 + Fast. A good performer. 497 +/ 498 O xsh_rs(O, Uint)(Uint state) 499 { 500 enum bits = Uint.sizeof * 8; 501 enum xtypebits = O.sizeof * 8; 502 enum sparebits = bits - xtypebits; 503 enum opbits = sparebits-5 >= 64 ? 5 504 : sparebits-4 >= 32 ? 4 505 : sparebits-3 >= 16 ? 3 506 : sparebits-2 >= 4 ? 2 507 : sparebits-1 >= 1 ? 1 508 : 0; 509 enum mask = (1 << opbits) - 1; 510 enum maxrandshift = mask; 511 enum topspare = opbits; 512 enum bottomspare = sparebits - topspare; 513 enum xshift = topspare + (xtypebits+maxrandshift)/2; 514 515 auto rshift = opbits ? size_t(state >> (bits - opbits)) & mask : 0; 516 state ^= state >> xshift; 517 O result = O(s >> (bottomspare - maxrandshift + rshift)); 518 return result; 519 } 520 /++ 521 + XSH RR -- high xorshift, followed by a random rotate 522 + 523 + Fast. A good performer. Slightly better statistically than XSH RS. 524 +/ 525 O xsh_rr(O, Uint)(Uint state) 526 { 527 enum bits = Uint.sizeof * 8; 528 enum xtypebits = O.sizeof * 8; 529 enum sparebits = bits - xtypebits; 530 enum wantedopbits = xtypebits >= 128 ? 7 531 : xtypebits >= 64 ? 6 532 : xtypebits >= 32 ? 5 533 : xtypebits >= 16 ? 4 534 : 3; 535 enum opbits = sparebits >= wantedopbits ? wantedopbits : sparebits; 536 enum amplifier = wantedopbits - opbits; 537 enum mask = (1 << opbits) - 1; 538 enum topspare = opbits; 539 enum bottomspare = sparebits - topspare; 540 enum xshift = (topspare + xtypebits)/2; 541 542 auto rot = opbits ? size_t(state >> (bits - opbits)) & mask : 0; 543 auto amprot = (rot << amplifier) & mask; 544 state ^= state >> xshift; 545 O result = cast(O)(state >> bottomspare); 546 import core.bitop: ror; 547 result = ror(result, cast(uint)amprot); 548 return result; 549 } 550 /++ 551 + RXS -- random xorshift 552 +/ 553 O rxs(O, Uint)(Uint state) 554 { 555 enum bits = Uint.sizeof * 8; 556 enum xtypebits = O.sizeof * 8; 557 enum shift = bits - xtypebits; 558 enum extrashift = (xtypebits - shift)/2; 559 enum rshift = shift > 64+8 ? (s >> (bits - 6)) & 63 560 : shift > 32+4 ? (s >> (bits - 5)) & 31 561 : shift > 16+2 ? (s >> (bits - 4)) & 15 562 : shift > 8+1 ? (s >> (bits - 3)) & 7 563 : shift > 4+1 ? (s >> (bits - 2)) & 3 564 : shift > 2+1 ? (s >> (bits - 1)) & 1 565 : 0; 566 state ^= state >> (shift + extrashift - rshift); 567 O result = state >> rshift; 568 return result; 569 } 570 /++ 571 + RXS M XS -- random xorshift, mcg multiply, fixed xorshift 572 + 573 + The most statistically powerful generator, but all those steps 574 + make it slower than some of the others. We give it the rottenest jobs. 575 + 576 + Because it's usually used in contexts where the state type and the 577 + result type are the same, it is a permutation and is thus invertible. 578 + We thus provide a function to invert it. This function is used to 579 + for the "inside out" generator used by the extended generator. 580 +/ 581 O rxs_m_xs_forward(O, Uint)(Uint state) 582 if(is(O == Uint)) 583 { 584 enum bits = Uint.sizeof * 8; 585 enum xtypebits = O.sizeof * 8; 586 enum opbits = xtypebits >= 128 ? 6 587 : xtypebits >= 64 ? 5 588 : xtypebits >= 32 ? 4 589 : xtypebits >= 16 ? 3 590 : 2; 591 enum shift = bits - xtypebits; 592 enum mask = (1 << opbits) - 1; 593 size_t rshift = opbits ? (state >> (bits - opbits)) & mask : 0; 594 state ^= state >> (opbits + rshift); 595 state *= mcg_multiplier!Uint; 596 O result = state >> shift; 597 result ^= result >> ((2U*xtypebits+2U)/3U); 598 return result; 599 } 600 /// ditto 601 O rxs_m_xs_reverse(O, Uint)(Uint state) 602 if(is(O == Uint)) 603 { 604 enum bits = Uint.sizeof * 8; 605 enum opbits = bits >= 128 ? 6 606 : bits >= 64 ? 5 607 : bits >= 32 ? 4 608 : bits >= 16 ? 3 609 : 2; 610 enum mask = (1 << opbits) - 1; 611 612 state = unxorshift(state, bits, (2U*bits+2U)/3U); 613 614 state *= mcg_unmultiplier!Uint; 615 616 auto rshift = opbits ? (state >> (bits - opbits)) & mask : 0; 617 state = unxorshift(s, bits, opbits + rshift); 618 619 return s; 620 } 621 /++ 622 + XSL RR -- fixed xorshift (to low bits), random rotate 623 + 624 + Useful for 128-bit types that are split across two CPU registers. 625 +/ 626 O xsl_rr(O, Uint)(Uint state) 627 { 628 enum bits = Uint.sizeof * 8; 629 enum xtypebits = O.sizeof * 8; 630 enum sparebits = bits - xtypebits; 631 enum wantedopbits = xtypebits >= 128 ? 7 632 : xtypebits >= 64 ? 6 633 : xtypebits >= 32 ? 5 634 : xtypebits >= 16 ? 4 635 : 3; 636 enum opbits = sparebits >= wantedopbits ? wantedopbits : sparebits; 637 enum amplifier = wantedopbits - opbits; 638 enum mask = (1 << opbits) - 1; 639 enum topspare = sparebits; 640 enum bottomspare = sparebits - topspare; 641 enum xshift = (topspare + xtypebits) / 2; 642 643 auto rot = opbits ? size_t(state >> (bits - opbits)) & mask : 0; 644 auto amprot = (rot << amplifier) & mask; 645 state ^= state >> xshift; 646 O result = state >> bottomspare; 647 result = rotr(result, amprot); 648 return result; 649 } 650 651 private template half_size(Uint) 652 { 653 static if (is(Uint == ucent)) 654 alias half_size = ulong; 655 else static if (is(Uint == ulong)) 656 alias half_size = uint; 657 else static if (is(Uint == uint)) 658 alias half_size = ushort; 659 else static if (is(Uint == ushort)) 660 alias half_size = ubyte; 661 else 662 static assert(0); 663 } 664 665 /++ 666 + XSL RR RR -- fixed xorshift (to low bits), random rotate (both parts) 667 + 668 + Useful for 128-bit types that are split across two CPU registers. 669 + If you really want an invertible 128-bit RNG, I guess this is the one. 670 +/ 671 672 O xsl_rr_rr(O, Uint)(Uint state) 673 if(is(O == Uint)) 674 { 675 alias H = half_size!Uint; 676 enum htypebits = H.sizeof * 8; 677 enum bits = Uint.sizeof * 8; 678 enum sparebits = bits - htypebits; 679 enum wantedopbits = htypebits >= 128 ? 7 680 : htypebits >= 64 ? 6 681 : htypebits >= 32 ? 5 682 : htypebits >= 16 ? 4 683 : 3; 684 enum opbits = sparebits >= wantedopbits ? wantedopbits : sparebits; 685 enum amplifier = wantedopbits - opbits; 686 enum mask = (1 << opbits) - 1; 687 enum topspare = sparebits; 688 enum xshift = (topspare + htypebits) / 2; 689 690 auto rot = opbits ? size_t(s >> (bits - opbits)) & mask : 0; 691 auto amprot = (rot << amplifier) & mask; 692 state ^= state >> xshift; 693 H lowbits = cast(H)s; 694 lowbits = rotr(lowbits, amprot); 695 H highbits = cast(H)(s >> topspare); 696 auto rot2 = lowbits & mask; 697 auto amprot2 = (rot2 << amplifier) & mask; 698 highbits = rotr(highbits, amprot2); 699 return (O(highbits) << topspare) ^ O(lowbits); 700 701 } 702 703 /++ 704 + XSH -- fixed xorshift (to high bits) 705 + 706 + Not available at 64-bits or less. 707 +/ 708 709 O xsh(O, Uint)(Uint state) if(Uint.sizeof > 8) 710 { 711 enum bits = Uint.sizeof * 8; 712 enum xtypebits = O.sizeof * 8; 713 enum sparebits = bits - xtypebits; 714 enum topspare = 0; 715 enum bottomspare = sparebits - topspare; 716 enum xshift = (topspare + xtypebits) / 2; 717 718 state ^= state >> xshift; 719 O result = state >> bottomspare; 720 return result; 721 } 722 723 /++ 724 + XSL -- fixed xorshift (to low bits) 725 + 726 + Not available at 64-bits or less. 727 +/ 728 729 O xsl(O, Uint)(Uint state) if(Uint.sizeof > 8) 730 { 731 enum bits = Uint.sizeof * 8; 732 enum xtypebits = O.sizeof * 8; 733 enum sparebits = bits - xtypebits; 734 enum topspare = sparebits; 735 enum bottomspare = sparebits - topspare; 736 enum xshift = (topspare + xtypebits) / 2; 737 738 state ^= state >> xshift; 739 O result = state >> bottomspare; 740 return result; 741 } 742 743 private alias AliasSeq(T...) = T; 744 745 @safe version(mir_random_test) unittest 746 { 747 748 foreach(RNG; AliasSeq!(pcg32,pcg32_unique,pcg32_oneseq,pcg32_fast, 749 pcg8_once_insecure,pcg16_once_insecure,pcg32_once_insecure,pcg64_once_insecure, 750 pcg8_oneseq_once_insecure,pcg16_oneseq_once_insecure,pcg32_oneseq_once_insecure, 751 pcg64_oneseq_once_insecure)) 752 { 753 static assert(isSaturatedRandomEngine!RNG); 754 auto gen = RNG(cast(RNG.Uint)unpredictableSeed); 755 gen(); 756 } 757 } 758 @safe version(mir_random_test) unittest 759 { 760 auto gen = pcg32(0x198c8585); 761 gen.skip(1000); 762 assert(gen()== 0xd187a760); 763 auto gen2 = pcg32(0x198c8585); 764 765 foreach(_; 0 .. 1000) 766 gen2(); 767 assert(gen2() == 0xd187a760); 768 assert(gen() == gen2()); 769 } 770 771 @nogc nothrow pure @safe unittest 772 { 773 foreach (ShouldHaveStaticBump; AliasSeq!(pcg32_oneseq, pcg32_fast, pcg32_oneseq_once_insecure)) 774 static assert (__traits(compiles, { enum e = ShouldHaveStaticBump.bump(1u); })); 775 776 foreach (ShouldLackStaticBump; AliasSeq!(pcg32, pcg32_unique, pcg32_once_insecure)) 777 static assert (!__traits(compiles, { enum e = ShouldLackStaticBump.bump(1u); })); 778 }