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