1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.rng.core.source64;
18
19 import java.math.BigInteger;
20 import java.util.SplittableRandom;
21 import java.util.concurrent.ThreadLocalRandom;
22 import java.util.function.Function;
23 import java.util.function.LongSupplier;
24 import java.util.function.Supplier;
25 import java.util.function.UnaryOperator;
26 import java.util.stream.IntStream;
27 import java.util.stream.LongStream;
28 import java.util.stream.Stream;
29 import org.apache.commons.rng.LongJumpableUniformRandomProvider;
30 import org.apache.commons.rng.RestorableUniformRandomProvider;
31 import org.apache.commons.rng.core.RandomAssert;
32 import org.apache.commons.rng.core.util.NumberFactory;
33 import org.junit.jupiter.api.Assertions;
34 import org.junit.jupiter.api.RepeatedTest;
35 import org.junit.jupiter.api.Test;
36 import org.junit.jupiter.api.TestInstance;
37 import org.junit.jupiter.api.TestInstance.Lifecycle;
38 import org.junit.jupiter.params.ParameterizedTest;
39 import org.junit.jupiter.params.provider.Arguments;
40 import org.junit.jupiter.params.provider.MethodSource;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 @TestInstance(Lifecycle.PER_CLASS)
103 abstract class AbstractLXMTest {
104
105
106
107
108 interface Mix {
109
110
111
112
113
114
115
116 long apply(long a, long b);
117 }
118
119
120
121
122
123
124
125
126 interface SubGen {
127
128
129
130
131
132 long stateAndUpdate();
133
134
135
136
137
138
139 SubGen copyAndJump();
140
141
142
143
144
145
146 SubGen copyAndLongJump();
147 }
148
149
150
151
152
153
154
155
156 static long mixStarStar(long a, long b) {
157 return Long.rotateLeft((a + b) * 5, 7) * 9;
158 }
159
160
161
162
163
164
165
166
167 static long mixLea64(long a, long b) {
168 return LXMSupport.lea64(a + b);
169 }
170
171
172
173
174 static class LCG64 implements SubGen {
175
176 private static final long M = LXMSupport.M64;
177
178 private final long a;
179
180 private long s;
181
182 private final int jumpPower;
183
184 private final int longJumpPower;
185
186
187
188
189
190
191
192 LCG64(long a, long s) {
193 this(a, s, 0, 32);
194 }
195
196
197
198
199
200
201
202 LCG64(long a, long s, int jumpPower, int longJumpPower) {
203 this.a = a | 1;
204 this.s = s;
205 this.jumpPower = jumpPower;
206 this.longJumpPower = longJumpPower;
207 }
208
209 @Override
210 public long stateAndUpdate() {
211 final long s0 = s;
212 s = M * s + a;
213 return s0;
214 }
215
216 @Override
217 public SubGen copyAndJump() {
218 final SubGen copy = new LCG64(a, s, jumpPower, longJumpPower);
219 s = LXMSupportTest.lcgAdvancePow2(s, M, a, jumpPower);
220 return copy;
221 }
222
223 @Override
224 public SubGen copyAndLongJump() {
225 final SubGen copy = new LCG64(a, s, jumpPower, longJumpPower);
226 s = LXMSupportTest.lcgAdvancePow2(s, M, a, longJumpPower);
227 return copy;
228 }
229 }
230
231
232
233
234 static class LCG128 implements SubGen {
235
236 private static final long ML = LXMSupport.M128L;
237
238 private final long ah;
239
240 private final long al;
241
242 private long sh;
243
244 private long sl;
245
246 private final int jumpPower;
247
248 private final int longJumpPower;
249
250
251
252
253
254
255
256
257
258 LCG128(long ah, long al, long sh, long sl) {
259 this(ah, al, sh, sl, 0, 64);
260 }
261
262
263
264
265
266
267
268
269
270 LCG128(long ah, long al, long sh, long sl, int jumpPower, int longJumpPower) {
271 this.ah = ah;
272 this.al = al | 1;
273 this.sh = sh;
274 this.sl = sl;
275 this.jumpPower = jumpPower;
276 this.longJumpPower = longJumpPower;
277 }
278
279 @Override
280 public long stateAndUpdate() {
281 final long s0 = sh;
282
283 final long u = ML * sl;
284
285 sh = (ML * sh) + LXMSupport.unsignedMultiplyHigh(ML, sl) + sl + ah;
286
287 sl = u + al;
288
289 if (Long.compareUnsigned(sl, u) < 0) {
290 ++sh;
291 }
292 return s0;
293 }
294
295 @Override
296 public SubGen copyAndJump() {
297 final SubGen copy = new LCG128(ah, al, sh, sl, jumpPower, longJumpPower);
298 final long nsl = LXMSupportTest.lcgAdvancePow2(sl, ML, al, jumpPower);
299 sh = LXMSupportTest.lcgAdvancePow2High(sh, sl, 1, ML, ah, al, jumpPower);
300 sl = nsl;
301 return copy;
302 }
303
304 @Override
305 public SubGen copyAndLongJump() {
306 final SubGen copy = new LCG128(ah, al, sh, sl, jumpPower, longJumpPower);
307 final long nsl = LXMSupportTest.lcgAdvancePow2(sl, ML, al, longJumpPower);
308 sh = LXMSupportTest.lcgAdvancePow2High(sh, sl, 1, ML, ah, al, longJumpPower);
309 sl = nsl;
310 return copy;
311 }
312 }
313
314
315
316
317
318
319
320 static class XBGXoRoShiRo128 extends AbstractXoRoShiRo128 implements SubGen {
321
322 private final boolean jump;
323
324
325
326
327
328
329 XBGXoRoShiRo128(long[] seed) {
330 super(seed);
331 jump = true;
332 }
333
334
335
336
337
338
339 XBGXoRoShiRo128(long seed0, long seed1, boolean jump) {
340 super(seed0, seed1);
341 this.jump = jump;
342 }
343
344
345
346
347
348
349 XBGXoRoShiRo128(XBGXoRoShiRo128 source) {
350
351
352
353
354
355
356 this(source.state0, source.state1, source.jump);
357 }
358
359 @Override
360 public long stateAndUpdate() {
361 final long s0 = state0;
362 next();
363 return s0;
364 }
365
366 @Override
367 public SubGen copyAndJump() {
368 return (SubGen) (jump ? super.jump() : copy());
369 }
370
371 @Override
372 public SubGen copyAndLongJump() {
373 return (SubGen) (jump ? super.longJump() : copy());
374 }
375
376 @Override
377 protected long nextOutput() {
378
379 return 0;
380 }
381
382 @Override
383 protected XBGXoRoShiRo128 copy() {
384 return new XBGXoRoShiRo128(this);
385 }
386 }
387
388
389
390
391
392
393
394 static class XBGXoShiRo256 extends AbstractXoShiRo256 implements SubGen {
395
396 private final boolean jump;
397
398
399
400
401
402
403 XBGXoShiRo256(long[] seed) {
404 super(seed);
405 jump = true;
406 }
407
408
409
410
411
412
413
414
415 XBGXoShiRo256(long seed0, long seed1, long seed2, long seed3, boolean jump) {
416 super(seed0, seed1, seed2, seed3);
417 this.jump = jump;
418 }
419
420
421
422
423
424
425 XBGXoShiRo256(XBGXoShiRo256 source) {
426 super(source);
427 jump = source.jump;
428 }
429
430 @Override
431 public long stateAndUpdate() {
432 final long s0 = state0;
433 next();
434 return s0;
435 }
436
437 @Override
438 public SubGen copyAndJump() {
439 return (SubGen) (jump ? super.jump() : copy());
440 }
441
442 @Override
443 public SubGen copyAndLongJump() {
444 return (SubGen) (jump ? super.longJump() : copy());
445 }
446
447 @Override
448 protected long nextOutput() {
449
450 return 0;
451 }
452
453 @Override
454 protected XBGXoShiRo256 copy() {
455 return new XBGXoShiRo256(this);
456 }
457 }
458
459
460
461
462
463
464
465
466
467
468
469 static class XBGXoRoShiRo1024 extends AbstractXoRoShiRo1024 implements SubGen {
470
471 private final boolean jump;
472
473 private long state0;
474
475
476
477
478
479
480 XBGXoRoShiRo1024(long[] seed) {
481 this(seed, true);
482 }
483
484
485
486
487
488 XBGXoRoShiRo1024(long[] seed, boolean jump) {
489 super(seed);
490 this.jump = jump;
491
492
493
494
495
496
497
498 final byte[] s = super.getStateInternal();
499 final byte[][] c = splitStateInternal(s, 17 * Long.BYTES);
500 final long[] tmp = NumberFactory.makeLongArray(c[0]);
501
502 tmp[16] = 15;
503 c[0] = NumberFactory.makeByteArray(tmp);
504 super.setStateInternal(composeStateInternal(c[0], c[1]));
505 }
506
507
508
509
510
511
512 XBGXoRoShiRo1024(XBGXoRoShiRo1024 source) {
513 super(source);
514 jump = source.jump;
515 }
516
517 @Override
518 public long stateAndUpdate() {
519 next();
520 return state0;
521 }
522
523 @Override
524 public SubGen copyAndJump() {
525 return (SubGen) (jump ? super.jump() : copy());
526 }
527
528 @Override
529 public SubGen copyAndLongJump() {
530 return (SubGen) (jump ? super.longJump() : copy());
531 }
532
533 @Override
534 protected long transform(long s0, long s15) {
535 this.state0 = s0;
536
537 return 0;
538 }
539
540 @Override
541 protected XBGXoRoShiRo1024 copy() {
542 return new XBGXoRoShiRo1024(this);
543 }
544 }
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559 static class LXMGenerator extends LongProvider implements LongJumpableUniformRandomProvider {
560
561 private final Mix mix;
562
563 private final SubGen lcg;
564
565 private final SubGen xbg;
566
567
568
569
570
571
572
573
574
575
576
577 LXMGenerator(Mix mix, SubGen lcg, SubGen xbg) {
578 this.lcg = lcg;
579 this.xbg = xbg;
580 this.mix = mix;
581 }
582
583 @Override
584 public long next() {
585 return mix.apply(lcg.stateAndUpdate(), xbg.stateAndUpdate());
586 }
587
588 @Override
589 public LXMGenerator jump() {
590 return new LXMGenerator(mix, lcg.copyAndJump(), xbg.copyAndJump());
591 }
592
593 @Override
594 public LXMGenerator longJump() {
595 return new LXMGenerator(mix, lcg.copyAndLongJump(), xbg.copyAndLongJump());
596 }
597 }
598
599
600
601
602 interface LXMGeneratorFactory {
603
604
605
606
607
608 int lcgSeedSize();
609
610
611
612
613
614
615 int xbgSeedSize();
616
617
618
619
620
621
622
623 default int seedSize() {
624 return lcgSeedSize() + xbgSeedSize();
625 }
626
627
628
629
630
631
632
633
634
635
636 LXMGenerator create(long[] seed);
637
638
639
640
641
642
643
644 default Mix getMix() {
645 return AbstractLXMTest::mixLea64;
646 }
647 }
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666 static class LCGTest {
667
668 private static final BigInteger TWO_POW_63 = BigInteger.ONE.shiftLeft(63);
669
670 private static final BigInteger M = BigInteger.ONE.shiftLeft(64).add(toUnsignedBigInteger(LXMSupport.M128L));
671
672 private static final BigInteger MOD = BigInteger.ONE.shiftLeft(128);
673
674
675 private static final int NO_JUMP = -1;
676
677 private static final int JUMP = 2;
678
679 private static final int LONG_JUMP = 4;
680
681 @RepeatedTest(value = 10)
682 void testLCG64DefaultJump() {
683 final SplittableRandom rng = new SplittableRandom();
684 final long state = rng.nextLong();
685 final long add = rng.nextLong();
686 final SubGen lcg1 = new LCG64(add, state);
687 final SubGen lcg2 = new LCG64(add, state, 0, 32);
688 for (int j = 0; j < 10; j++) {
689 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
690 () -> String.format("seed %d,%d", state, add));
691 }
692 lcg1.copyAndJump();
693 lcg2.copyAndJump();
694 for (int j = 0; j < 10; j++) {
695 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
696 () -> String.format("seed %d,%d", state, add));
697 }
698 lcg1.copyAndLongJump();
699 lcg2.copyAndLongJump();
700 for (int j = 0; j < 10; j++) {
701 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
702 () -> String.format("seed %d,%d", state, add));
703 }
704 }
705
706 @RepeatedTest(value = 10)
707 void testLCG64() {
708 final SplittableRandom rng = new SplittableRandom();
709 final long state = rng.nextLong();
710 final long add = rng.nextLong();
711 long s = state;
712 final long a = add | 1;
713 final SubGen lcg = new LCG64(add, state, NO_JUMP, NO_JUMP);
714 for (int j = 0; j < 10; j++) {
715 Assertions.assertEquals(s, lcg.stateAndUpdate(),
716 () -> String.format("seed %d,%d", state, add));
717 s = LXMSupport.M64 * s + a;
718 }
719 }
720
721 @RepeatedTest(value = 10)
722 void testLCG64Jump() {
723 final SplittableRandom rng = new SplittableRandom();
724 final long state = rng.nextLong();
725 final long add = rng.nextLong();
726 final Supplier<String> msg = () -> String.format("seed %d,%d", state, add);
727 long s = state;
728 final long a = add | 1;
729 final SubGen lcg = new LCG64(add, state, JUMP, LONG_JUMP);
730
731 final SubGen copy1 = lcg.copyAndJump();
732 for (int j = 1 << JUMP; j-- != 0;) {
733 Assertions.assertEquals(s, copy1.stateAndUpdate(), msg);
734 s = LXMSupport.M64 * s + a;
735 }
736 Assertions.assertEquals(s, lcg.stateAndUpdate(), msg);
737 s = LXMSupport.M64 * s + a;
738
739 final SubGen copy2 = lcg.copyAndLongJump();
740 for (int j = 1 << LONG_JUMP; j-- != 0;) {
741 Assertions.assertEquals(s, copy2.stateAndUpdate(), msg);
742 s = LXMSupport.M64 * s + a;
743 }
744 Assertions.assertEquals(s, lcg.stateAndUpdate(), msg);
745 }
746
747 @RepeatedTest(value = 10)
748 void testLCG128DefaultJump() {
749 final SplittableRandom rng = new SplittableRandom();
750 final long stateh = rng.nextLong();
751 final long statel = rng.nextLong();
752 final long addh = rng.nextLong();
753 final long addl = rng.nextLong();
754 final SubGen lcg1 = new LCG128(addh, addl, stateh, statel);
755 final SubGen lcg2 = new LCG128(addh, addl, stateh, statel, 0, 64);
756 for (int j = 0; j < 10; j++) {
757 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
758 () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl));
759 }
760 lcg1.copyAndJump();
761 lcg2.copyAndJump();
762 for (int j = 0; j < 10; j++) {
763 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
764 () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl));
765 }
766 lcg1.copyAndLongJump();
767 lcg2.copyAndLongJump();
768 for (int j = 0; j < 10; j++) {
769 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
770 () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl));
771 }
772 }
773
774 @RepeatedTest(value = 10)
775 void testLCG128() {
776 final SplittableRandom rng = new SplittableRandom();
777 final long stateh = rng.nextLong();
778 final long statel = rng.nextLong();
779 final long addh = rng.nextLong();
780 final long addl = rng.nextLong();
781 BigInteger s = toUnsignedBigInteger(stateh).shiftLeft(64).add(toUnsignedBigInteger(statel));
782 final BigInteger a = toUnsignedBigInteger(addh).shiftLeft(64).add(toUnsignedBigInteger(addl | 1));
783 final SubGen lcg = new LCG128(addh, addl, stateh, statel, NO_JUMP, NO_JUMP);
784 for (int j = 0; j < 10; j++) {
785 Assertions.assertEquals(s.shiftRight(64).longValue(), lcg.stateAndUpdate(),
786 () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl));
787 s = M.multiply(s).add(a).mod(MOD);
788 }
789 }
790
791 @RepeatedTest(value = 10)
792 void testLCG128Jump() {
793 final SplittableRandom rng = new SplittableRandom();
794 final long stateh = rng.nextLong();
795 final long statel = rng.nextLong();
796 final long addh = rng.nextLong();
797 final long addl = rng.nextLong();
798 final Supplier<String> msg = () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl);
799 BigInteger s = toUnsignedBigInteger(stateh).shiftLeft(64).add(toUnsignedBigInteger(statel));
800 final BigInteger a = toUnsignedBigInteger(addh).shiftLeft(64).add(toUnsignedBigInteger(addl | 1));
801 final SubGen lcg = new LCG128(addh, addl, stateh, statel, JUMP, LONG_JUMP);
802
803 final SubGen copy1 = lcg.copyAndJump();
804 for (int j = 1 << JUMP; j-- != 0;) {
805 Assertions.assertEquals(s.shiftRight(64).longValue(), copy1.stateAndUpdate(), msg);
806 s = M.multiply(s).add(a).mod(MOD);
807 }
808 Assertions.assertEquals(s.shiftRight(64).longValue(), lcg.stateAndUpdate(), msg);
809 s = M.multiply(s).add(a).mod(MOD);
810
811 final SubGen copy2 = lcg.copyAndLongJump();
812 for (int j = 1 << LONG_JUMP; j-- != 0;) {
813 Assertions.assertEquals(s.shiftRight(64).longValue(), copy2.stateAndUpdate(), msg);
814 s = M.multiply(s).add(a).mod(MOD);
815 }
816 Assertions.assertEquals(s.shiftRight(64).longValue(), lcg.stateAndUpdate(), msg);
817 }
818
819
820
821
822
823
824
825 private static BigInteger toUnsignedBigInteger(long v) {
826 return v < 0 ?
827 TWO_POW_63.add(BigInteger.valueOf(v & Long.MAX_VALUE)) :
828 BigInteger.valueOf(v);
829 }
830 }
831
832
833
834
835
836
837
838
839
840
841 static class XBGTest {
842
843 @RepeatedTest(value = 5)
844 void testXBGXoRoShiRo128NoJump() {
845 final SplittableRandom r = new SplittableRandom();
846 assertNoJump(new XBGXoRoShiRo128(r.nextLong(), r.nextLong(), false));
847 }
848
849 @RepeatedTest(value = 5)
850 void testXBGXoShiRo256NoJump() {
851 final SplittableRandom r = new SplittableRandom();
852 assertNoJump(new XBGXoShiRo256(r.nextLong(), r.nextLong(), r.nextLong(), r.nextLong(), false));
853 }
854
855 @RepeatedTest(value = 5)
856 void testXBGXoRoShiRo1024NoJump() {
857 final SplittableRandom r = new SplittableRandom();
858 assertNoJump(new XBGXoRoShiRo1024(r.longs(16).toArray(), false));
859 }
860
861 void assertNoJump(SubGen g) {
862 final SubGen g1 = g.copyAndJump();
863 Assertions.assertNotSame(g, g1);
864 for (int i = 0; i < 10; i++) {
865 Assertions.assertEquals(g.stateAndUpdate(), g1.stateAndUpdate());
866 }
867 final SubGen g2 = g.copyAndLongJump();
868 Assertions.assertNotSame(g, g2);
869 for (int i = 0; i < 10; i++) {
870 Assertions.assertEquals(g.stateAndUpdate(), g2.stateAndUpdate());
871 }
872 }
873
874 @RepeatedTest(value = 5)
875 void testXBGXoRoShiRo128Jump() {
876 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 2, XBGXoRoShiRo128::new, SubGen::copyAndJump);
877 }
878
879 @RepeatedTest(value = 5)
880 void testXBGXoRoShiRo128LongJump() {
881 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 2, XBGXoRoShiRo128::new, SubGen::copyAndLongJump);
882 }
883
884 @RepeatedTest(value = 5)
885 void testXBGXoShiRo256Jump() {
886 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 4, XBGXoShiRo256::new, SubGen::copyAndJump);
887 }
888
889 @RepeatedTest(value = 5)
890 void testXBGXShiRo256LongJump() {
891 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 4, XBGXoShiRo256::new, SubGen::copyAndLongJump);
892 }
893
894 @RepeatedTest(value = 5)
895 void testXBGXoRoShiRo1024Jump() {
896 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 16, XBGXoRoShiRo1024::new, SubGen::copyAndJump);
897 }
898
899 @RepeatedTest(value = 5)
900 void testXBGXoRoShiRo1024LongJump() {
901 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 16, XBGXoRoShiRo1024::new, SubGen::copyAndLongJump);
902 }
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917 private static void assertJumpAndCycle(long testSeed, int seedSize,
918 Function<long[], SubGen> factory, UnaryOperator<SubGen> jump) {
919 final SplittableRandom r = new SplittableRandom(testSeed);
920 final long[] seed = r.longs(seedSize).toArray();
921 final int[] steps = createSteps(r, seedSize);
922 final int cycles = 2 * seedSize;
923
924 final SubGen rng1 = factory.apply(seed);
925 final SubGen rng2 = factory.apply(seed);
926
927
928 for (int i = 0; i < steps.length; i++) {
929 final int step = steps[i];
930 final int index = i;
931
932 final SubGen copy = jump.apply(rng1);
933
934 for (int j = 0; j < step; j++) {
935 Assertions.assertEquals(rng2.stateAndUpdate(), copy.stateAndUpdate(),
936 () -> String.format("[%d] Incorrect trailing copy, seed=%d", index, testSeed));
937
938 rng1.stateAndUpdate();
939 }
940
941 jump.apply(rng2);
942 for (int j = 0; j < cycles; j++) {
943 Assertions.assertEquals(rng1.stateAndUpdate(), rng2.stateAndUpdate(),
944 () -> String.format("[%d] Incorrect after catch up jump, seed=%d", index, testSeed));
945 }
946 }
947 }
948 }
949
950
951
952
953
954
955
956
957
958
959
960 abstract LXMGeneratorFactory getFactory();
961
962
963
964
965
966
967
968 abstract LongJumpableUniformRandomProvider create(long[] seed);
969
970
971
972
973
974
975
976
977
978
979
980
981 abstract Stream<Arguments> getReferenceData();
982
983
984
985
986
987
988 @ParameterizedTest
989 @MethodSource(value = "getReferenceData")
990 final void testReferenceData(long[] seed, long[] expected) {
991 RandomAssert.assertEquals(expected, create(seed));
992 }
993
994
995
996
997
998 @ParameterizedTest
999 @MethodSource(value = "getReferenceData")
1000 final void testReferenceDataWithComposite(long[] seed, long[] expected) {
1001 RandomAssert.assertEquals(expected, getFactory().create(seed));
1002 }
1003
1004
1005
1006
1007
1008
1009 @RepeatedTest(value = 5)
1010 final void testInitialOutput() {
1011 final long[] seed = createRandomSeed();
1012
1013 final long s = seed[getFactory().lcgSeedSize() / 2];
1014
1015 final long t = seed[getFactory().lcgSeedSize()];
1016 Assertions.assertEquals(getFactory().getMix().apply(s, t), create(seed).nextLong());
1017 }
1018
1019 @Test
1020 final void testConstructorWithZeroSeedIsPartiallyFunctional() {
1021
1022
1023
1024
1025 final int seedSize = getFactory().seedSize();
1026 RandomAssert.assertNextLongNonZeroOutput(create(new long[seedSize]), 0, seedSize);
1027 }
1028
1029 @ParameterizedTest
1030 @MethodSource(value = "getReferenceData")
1031 final void testConstructorWithoutFullLengthSeed(long[] seed) {
1032
1033 final int seedSize = getFactory().seedSize();
1034 RandomAssert.assertNextLongNonZeroOutput(create(new long[] {seed[0]}),
1035 seedSize, seedSize);
1036 }
1037
1038 @RepeatedTest(value = 5)
1039 final void testConstructorIgnoresFinalAddParameterSeedBit() {
1040 final long[] seed1 = createRandomSeed();
1041 final long[] seed2 = seed1.clone();
1042 final int seedSize = getFactory().seedSize();
1043
1044 final int index = getFactory().lcgSeedSize() / 2 - 1;
1045 seed1[index] &= -1L << 1;
1046 seed2[index] |= 1;
1047 Assertions.assertEquals(1, seed1[index] ^ seed2[index]);
1048 final LongJumpableUniformRandomProvider rng1 = create(seed1);
1049 final LongJumpableUniformRandomProvider rng2 = create(seed2);
1050 RandomAssert.assertNextLongEquals(seedSize * 2, rng1, rng2);
1051 }
1052
1053 @ParameterizedTest
1054 @MethodSource(value = "getReferenceData")
1055 final void testJump(long[] seed, long[] expected) {
1056 final long[] expectedAfter = createExpectedSequence(seed, expected.length, false);
1057 RandomAssert.assertJumpEquals(expected, expectedAfter, create(seed));
1058 }
1059
1060 @ParameterizedTest
1061 @MethodSource(value = "getReferenceData")
1062 final void testLongJump(long[] seed, long[] expected) {
1063 final long[] expectedAfter = createExpectedSequence(seed, expected.length, true);
1064 RandomAssert.assertLongJumpEquals(expected, expectedAfter, create(seed));
1065 }
1066
1067 @RepeatedTest(value = 5)
1068 final void testJumpAndOutput() {
1069 assertJumpAndOutput(false, ThreadLocalRandom.current().nextLong());
1070 }
1071
1072 @RepeatedTest(value = 5)
1073 final void testLongJumpAndOutput() {
1074 assertJumpAndOutput(true, ThreadLocalRandom.current().nextLong());
1075 }
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097 private void assertJumpAndOutput(boolean longJump, long testSeed) {
1098 final SplittableRandom r = new SplittableRandom(testSeed);
1099 final long[] seed = createRandomSeed(r::nextLong);
1100 final int[] steps = createSteps(r);
1101 final int cycles = getFactory().seedSize();
1102
1103 LongJumpableUniformRandomProvider rng1 = create(seed);
1104 LongJumpableUniformRandomProvider rng2 = getFactory().create(seed);
1105
1106 final UnaryOperator<LongJumpableUniformRandomProvider> jump = longJump ?
1107 x -> (LongJumpableUniformRandomProvider) x.longJump() :
1108 x -> (LongJumpableUniformRandomProvider) x.jump();
1109
1110
1111 for (int i = 0; i < steps.length; i++) {
1112 final int step = steps[i];
1113 final int index = i;
1114
1115 LongJumpableUniformRandomProvider copy = jump.apply(rng1);
1116
1117 for (int j = 0; j < step; j++) {
1118 Assertions.assertEquals(rng2.nextLong(), copy.nextLong(),
1119 () -> String.format("[%d] Incorrect trailing copy, seed=%d", index, testSeed));
1120
1121 rng1.nextLong();
1122 }
1123
1124 jump.apply(rng2);
1125 for (int j = 0; j < cycles; j++) {
1126 Assertions.assertEquals(rng1.nextLong(), rng2.nextLong(),
1127 () -> String.format("[%d] Incorrect after catch up jump, seed=%d", index, testSeed));
1128 }
1129
1130
1131 copy = rng1;
1132 rng1 = rng2;
1133 rng2 = copy;
1134 }
1135 }
1136
1137 @RepeatedTest(value = 5)
1138 final void testSaveRestoreAfterJump() {
1139 assertSaveRestoreAfterJump(false, ThreadLocalRandom.current().nextLong());
1140 }
1141
1142 @RepeatedTest(value = 5)
1143 final void testSaveRestoreAfterLongJump() {
1144 assertSaveRestoreAfterJump(true, ThreadLocalRandom.current().nextLong());
1145 }
1146
1147
1148
1149
1150
1151
1152
1153
1154 private void assertSaveRestoreAfterJump(boolean longJump, long testSeed) {
1155 final SplittableRandom r = new SplittableRandom(testSeed);
1156 final int cycles = getFactory().seedSize();
1157
1158 final UnaryOperator<LongJumpableUniformRandomProvider> jump = longJump ?
1159 x -> (LongJumpableUniformRandomProvider) x.longJump() :
1160 x -> (LongJumpableUniformRandomProvider) x.jump();
1161
1162
1163 final LongJumpableUniformRandomProvider rng1 = create(createRandomSeed(r::nextLong));
1164 final LongJumpableUniformRandomProvider rng2 = create(createRandomSeed(r::nextLong));
1165 jump.apply(rng1);
1166 jump.apply(rng2);
1167
1168
1169 RestorableUniformRandomProvider g1 = (RestorableUniformRandomProvider) rng1;
1170 RestorableUniformRandomProvider g2 = (RestorableUniformRandomProvider) rng2;
1171 g2.restoreState(g1.saveState());
1172
1173
1174 RandomAssert.assertNextLongEquals(cycles, rng1, rng2);
1175
1176
1177 jump.apply(rng1);
1178 jump.apply(rng2);
1179
1180 RandomAssert.assertNextLongEquals(cycles, rng1, rng2);
1181 }
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193 private long[] createExpectedSequence(long[] seed, int length, boolean longJump) {
1194 final LXMGenerator rng = getFactory().create(seed);
1195 if (longJump) {
1196 rng.longJump();
1197 } else {
1198 rng.jump();
1199 }
1200 return LongStream.generate(rng::nextLong).limit(length).toArray();
1201 }
1202
1203
1204
1205
1206
1207
1208 private long[] createRandomSeed() {
1209 return createRandomSeed(new SplittableRandom()::nextLong);
1210 }
1211
1212
1213
1214
1215
1216
1217
1218
1219 private long[] createRandomSeed(LongSupplier gen) {
1220 return LongStream.generate(gen).limit(getFactory().seedSize()).toArray();
1221 }
1222
1223
1224
1225
1226
1227
1228
1229
1230 private int[] createSteps(SplittableRandom rng) {
1231 return createSteps(rng, getFactory().seedSize());
1232 }
1233
1234
1235
1236
1237
1238
1239
1240
1241 private static int[] createSteps(SplittableRandom rng, int seedSize) {
1242 final int[] steps = IntStream.rangeClosed(1, seedSize).toArray();
1243
1244 for (int i = steps.length; i > 1; i--) {
1245 final int j = rng.nextInt(i);
1246 final int tmp = steps[i - 1];
1247 steps[i - 1] = steps[j];
1248 steps[j] = tmp;
1249 }
1250 return steps;
1251 }
1252 }