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