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 }