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 M = BigInteger.ONE.shiftLeft(64).add(toUnsignedBigInteger(LXMSupport.M128L));
669
670 private static final BigInteger MOD = BigInteger.ONE.shiftLeft(128);
671
672
673 private static final int NO_JUMP = -1;
674
675 private static final int JUMP = 2;
676
677 private static final int LONG_JUMP = 4;
678
679 @RepeatedTest(value = 10)
680 void testLCG64DefaultJump() {
681 final SplittableRandom rng = new SplittableRandom();
682 final long state = rng.nextLong();
683 final long add = rng.nextLong();
684 final SubGen lcg1 = new LCG64(add, state);
685 final SubGen lcg2 = new LCG64(add, state, 0, 32);
686 for (int j = 0; j < 10; j++) {
687 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
688 () -> String.format("seed %d,%d", state, add));
689 }
690 lcg1.copyAndJump();
691 lcg2.copyAndJump();
692 for (int j = 0; j < 10; j++) {
693 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
694 () -> String.format("seed %d,%d", state, add));
695 }
696 lcg1.copyAndLongJump();
697 lcg2.copyAndLongJump();
698 for (int j = 0; j < 10; j++) {
699 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
700 () -> String.format("seed %d,%d", state, add));
701 }
702 }
703
704 @RepeatedTest(value = 10)
705 void testLCG64() {
706 final SplittableRandom rng = new SplittableRandom();
707 final long state = rng.nextLong();
708 final long add = rng.nextLong();
709 long s = state;
710 final long a = add | 1;
711 final SubGen lcg = new LCG64(add, state, NO_JUMP, NO_JUMP);
712 for (int j = 0; j < 10; j++) {
713 Assertions.assertEquals(s, lcg.stateAndUpdate(),
714 () -> String.format("seed %d,%d", state, add));
715 s = LXMSupport.M64 * s + a;
716 }
717 }
718
719 @RepeatedTest(value = 10)
720 void testLCG64Jump() {
721 final SplittableRandom rng = new SplittableRandom();
722 final long state = rng.nextLong();
723 final long add = rng.nextLong();
724 final Supplier<String> msg = () -> String.format("seed %d,%d", state, add);
725 long s = state;
726 final long a = add | 1;
727 final SubGen lcg = new LCG64(add, state, JUMP, LONG_JUMP);
728
729 final SubGen copy1 = lcg.copyAndJump();
730 for (int j = 1 << JUMP; j-- != 0;) {
731 Assertions.assertEquals(s, copy1.stateAndUpdate(), msg);
732 s = LXMSupport.M64 * s + a;
733 }
734 Assertions.assertEquals(s, lcg.stateAndUpdate(), msg);
735 s = LXMSupport.M64 * s + a;
736
737 final SubGen copy2 = lcg.copyAndLongJump();
738 for (int j = 1 << LONG_JUMP; j-- != 0;) {
739 Assertions.assertEquals(s, copy2.stateAndUpdate(), msg);
740 s = LXMSupport.M64 * s + a;
741 }
742 Assertions.assertEquals(s, lcg.stateAndUpdate(), msg);
743 }
744
745 @RepeatedTest(value = 10)
746 void testLCG128DefaultJump() {
747 final SplittableRandom rng = new SplittableRandom();
748 final long stateh = rng.nextLong();
749 final long statel = rng.nextLong();
750 final long addh = rng.nextLong();
751 final long addl = rng.nextLong();
752 final SubGen lcg1 = new LCG128(addh, addl, stateh, statel);
753 final SubGen lcg2 = new LCG128(addh, addl, stateh, statel, 0, 64);
754 for (int j = 0; j < 10; j++) {
755 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
756 () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl));
757 }
758 lcg1.copyAndJump();
759 lcg2.copyAndJump();
760 for (int j = 0; j < 10; j++) {
761 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
762 () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl));
763 }
764 lcg1.copyAndLongJump();
765 lcg2.copyAndLongJump();
766 for (int j = 0; j < 10; j++) {
767 Assertions.assertEquals(lcg1.stateAndUpdate(), lcg2.stateAndUpdate(),
768 () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl));
769 }
770 }
771
772 @RepeatedTest(value = 10)
773 void testLCG128() {
774 final SplittableRandom rng = new SplittableRandom();
775 final long stateh = rng.nextLong();
776 final long statel = rng.nextLong();
777 final long addh = rng.nextLong();
778 final long addl = rng.nextLong();
779 BigInteger s = toUnsignedBigInteger(stateh).shiftLeft(64).add(toUnsignedBigInteger(statel));
780 final BigInteger a = toUnsignedBigInteger(addh).shiftLeft(64).add(toUnsignedBigInteger(addl | 1));
781 final SubGen lcg = new LCG128(addh, addl, stateh, statel, NO_JUMP, NO_JUMP);
782 for (int j = 0; j < 10; j++) {
783 Assertions.assertEquals(s.shiftRight(64).longValue(), lcg.stateAndUpdate(),
784 () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl));
785 s = M.multiply(s).add(a).mod(MOD);
786 }
787 }
788
789 @RepeatedTest(value = 10)
790 void testLCG128Jump() {
791 final SplittableRandom rng = new SplittableRandom();
792 final long stateh = rng.nextLong();
793 final long statel = rng.nextLong();
794 final long addh = rng.nextLong();
795 final long addl = rng.nextLong();
796 final Supplier<String> msg = () -> String.format("seed %d,%d,%d,%d", stateh, statel, addh, addl);
797 BigInteger s = toUnsignedBigInteger(stateh).shiftLeft(64).add(toUnsignedBigInteger(statel));
798 final BigInteger a = toUnsignedBigInteger(addh).shiftLeft(64).add(toUnsignedBigInteger(addl | 1));
799 final SubGen lcg = new LCG128(addh, addl, stateh, statel, JUMP, LONG_JUMP);
800
801 final SubGen copy1 = lcg.copyAndJump();
802 for (int j = 1 << JUMP; j-- != 0;) {
803 Assertions.assertEquals(s.shiftRight(64).longValue(), copy1.stateAndUpdate(), msg);
804 s = M.multiply(s).add(a).mod(MOD);
805 }
806 Assertions.assertEquals(s.shiftRight(64).longValue(), lcg.stateAndUpdate(), msg);
807 s = M.multiply(s).add(a).mod(MOD);
808
809 final SubGen copy2 = lcg.copyAndLongJump();
810 for (int j = 1 << LONG_JUMP; j-- != 0;) {
811 Assertions.assertEquals(s.shiftRight(64).longValue(), copy2.stateAndUpdate(), msg);
812 s = M.multiply(s).add(a).mod(MOD);
813 }
814 Assertions.assertEquals(s.shiftRight(64).longValue(), lcg.stateAndUpdate(), msg);
815 }
816
817
818
819
820
821
822
823 private static BigInteger toUnsignedBigInteger(long v) {
824
825 return LXMSupportTest.toUnsignedBigInteger(v);
826 }
827 }
828
829
830
831
832
833
834
835
836
837
838 static class XBGTest {
839
840 @RepeatedTest(value = 5)
841 void testXBGXoRoShiRo128NoJump() {
842 final SplittableRandom r = new SplittableRandom();
843 assertNoJump(new XBGXoRoShiRo128(r.nextLong(), r.nextLong(), false));
844 }
845
846 @RepeatedTest(value = 5)
847 void testXBGXoShiRo256NoJump() {
848 final SplittableRandom r = new SplittableRandom();
849 assertNoJump(new XBGXoShiRo256(r.nextLong(), r.nextLong(), r.nextLong(), r.nextLong(), false));
850 }
851
852 @RepeatedTest(value = 5)
853 void testXBGXoRoShiRo1024NoJump() {
854 final SplittableRandom r = new SplittableRandom();
855 assertNoJump(new XBGXoRoShiRo1024(r.longs(16).toArray(), false));
856 }
857
858 void assertNoJump(SubGen g) {
859 final SubGen g1 = g.copyAndJump();
860 Assertions.assertNotSame(g, g1);
861 for (int i = 0; i < 10; i++) {
862 Assertions.assertEquals(g.stateAndUpdate(), g1.stateAndUpdate());
863 }
864 final SubGen g2 = g.copyAndLongJump();
865 Assertions.assertNotSame(g, g2);
866 for (int i = 0; i < 10; i++) {
867 Assertions.assertEquals(g.stateAndUpdate(), g2.stateAndUpdate());
868 }
869 }
870
871 @RepeatedTest(value = 5)
872 void testXBGXoRoShiRo128Jump() {
873 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 2, XBGXoRoShiRo128::new, SubGen::copyAndJump);
874 }
875
876 @RepeatedTest(value = 5)
877 void testXBGXoRoShiRo128LongJump() {
878 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 2, XBGXoRoShiRo128::new, SubGen::copyAndLongJump);
879 }
880
881 @RepeatedTest(value = 5)
882 void testXBGXoShiRo256Jump() {
883 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 4, XBGXoShiRo256::new, SubGen::copyAndJump);
884 }
885
886 @RepeatedTest(value = 5)
887 void testXBGXShiRo256LongJump() {
888 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 4, XBGXoShiRo256::new, SubGen::copyAndLongJump);
889 }
890
891 @RepeatedTest(value = 5)
892 void testXBGXoRoShiRo1024Jump() {
893 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 16, XBGXoRoShiRo1024::new, SubGen::copyAndJump);
894 }
895
896 @RepeatedTest(value = 5)
897 void testXBGXoRoShiRo1024LongJump() {
898 assertJumpAndCycle(ThreadLocalRandom.current().nextLong(), 16, XBGXoRoShiRo1024::new, SubGen::copyAndLongJump);
899 }
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914 private static void assertJumpAndCycle(long testSeed, int seedSize,
915 Function<long[], SubGen> factory, UnaryOperator<SubGen> jump) {
916 final SplittableRandom r = new SplittableRandom(testSeed);
917 final long[] seed = r.longs(seedSize).toArray();
918 final int[] steps = createSteps(r, seedSize);
919 final int cycles = 2 * seedSize;
920
921 final SubGen rng1 = factory.apply(seed);
922 final SubGen rng2 = factory.apply(seed);
923
924
925 for (int i = 0; i < steps.length; i++) {
926 final int step = steps[i];
927 final int index = i;
928
929 final SubGen copy = jump.apply(rng1);
930
931 for (int j = 0; j < step; j++) {
932 Assertions.assertEquals(rng2.stateAndUpdate(), copy.stateAndUpdate(),
933 () -> String.format("[%d] Incorrect trailing copy, seed=%d", index, testSeed));
934
935 rng1.stateAndUpdate();
936 }
937
938 jump.apply(rng2);
939 for (int j = 0; j < cycles; j++) {
940 Assertions.assertEquals(rng1.stateAndUpdate(), rng2.stateAndUpdate(),
941 () -> String.format("[%d] Incorrect after catch up jump, seed=%d", index, testSeed));
942 }
943 }
944 }
945 }
946
947
948
949
950
951
952
953
954
955
956
957 abstract LXMGeneratorFactory getFactory();
958
959
960
961
962
963
964
965 abstract LongJumpableUniformRandomProvider create(long[] seed);
966
967
968
969
970
971
972
973
974
975
976
977
978 abstract Stream<Arguments> getReferenceData();
979
980
981
982
983
984
985 @ParameterizedTest
986 @MethodSource(value = "getReferenceData")
987 final void testReferenceData(long[] seed, long[] expected) {
988 RandomAssert.assertEquals(expected, create(seed));
989 }
990
991
992
993
994
995 @ParameterizedTest
996 @MethodSource(value = "getReferenceData")
997 final void testReferenceDataWithComposite(long[] seed, long[] expected) {
998 RandomAssert.assertEquals(expected, getFactory().create(seed));
999 }
1000
1001
1002
1003
1004
1005
1006 @RepeatedTest(value = 5)
1007 final void testInitialOutput() {
1008 final long[] seed = createRandomSeed();
1009
1010 final long s = seed[getFactory().lcgSeedSize() / 2];
1011
1012 final long t = seed[getFactory().lcgSeedSize()];
1013 Assertions.assertEquals(getFactory().getMix().apply(s, t), create(seed).nextLong());
1014 }
1015
1016 @Test
1017 final void testConstructorWithZeroSeedIsPartiallyFunctional() {
1018
1019
1020
1021
1022 final int seedSize = getFactory().seedSize();
1023 RandomAssert.assertNextLongNonZeroOutput(create(new long[seedSize]), 0, seedSize);
1024 }
1025
1026 @ParameterizedTest
1027 @MethodSource(value = "getReferenceData")
1028 final void testConstructorWithoutFullLengthSeed(long[] seed) {
1029
1030 final int seedSize = getFactory().seedSize();
1031 RandomAssert.assertNextLongNonZeroOutput(create(new long[] {seed[0]}),
1032 seedSize, seedSize);
1033 }
1034
1035 @RepeatedTest(value = 5)
1036 final void testConstructorIgnoresFinalAddParameterSeedBit() {
1037 final long[] seed1 = createRandomSeed();
1038 final long[] seed2 = seed1.clone();
1039 final int seedSize = getFactory().seedSize();
1040
1041 final int index = getFactory().lcgSeedSize() / 2 - 1;
1042 seed1[index] &= -1L << 1;
1043 seed2[index] |= 1;
1044 Assertions.assertEquals(1, seed1[index] ^ seed2[index]);
1045 final LongJumpableUniformRandomProvider rng1 = create(seed1);
1046 final LongJumpableUniformRandomProvider rng2 = create(seed2);
1047 RandomAssert.assertNextLongEquals(seedSize * 2, rng1, rng2);
1048 }
1049
1050 @ParameterizedTest
1051 @MethodSource(value = "getReferenceData")
1052 final void testJump(long[] seed, long[] expected) {
1053 final long[] expectedAfter = createExpectedSequence(seed, expected.length, false);
1054 RandomAssert.assertJumpEquals(expected, expectedAfter, create(seed));
1055 }
1056
1057 @ParameterizedTest
1058 @MethodSource(value = "getReferenceData")
1059 final void testLongJump(long[] seed, long[] expected) {
1060 final long[] expectedAfter = createExpectedSequence(seed, expected.length, true);
1061 RandomAssert.assertLongJumpEquals(expected, expectedAfter, create(seed));
1062 }
1063
1064 @RepeatedTest(value = 5)
1065 final void testJumpAndOutput() {
1066 assertJumpAndOutput(false, ThreadLocalRandom.current().nextLong());
1067 }
1068
1069 @RepeatedTest(value = 5)
1070 final void testLongJumpAndOutput() {
1071 assertJumpAndOutput(true, ThreadLocalRandom.current().nextLong());
1072 }
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094 private void assertJumpAndOutput(boolean longJump, long testSeed) {
1095 final SplittableRandom r = new SplittableRandom(testSeed);
1096 final long[] seed = createRandomSeed(r::nextLong);
1097 final int[] steps = createSteps(r);
1098 final int cycles = getFactory().seedSize();
1099
1100 LongJumpableUniformRandomProvider rng1 = create(seed);
1101 LongJumpableUniformRandomProvider rng2 = getFactory().create(seed);
1102
1103 final UnaryOperator<LongJumpableUniformRandomProvider> jump = longJump ?
1104 x -> (LongJumpableUniformRandomProvider) x.longJump() :
1105 x -> (LongJumpableUniformRandomProvider) x.jump();
1106
1107
1108 for (int i = 0; i < steps.length; i++) {
1109 final int step = steps[i];
1110 final int index = i;
1111
1112 LongJumpableUniformRandomProvider copy = jump.apply(rng1);
1113
1114 for (int j = 0; j < step; j++) {
1115 Assertions.assertEquals(rng2.nextLong(), copy.nextLong(),
1116 () -> String.format("[%d] Incorrect trailing copy, seed=%d", index, testSeed));
1117
1118 rng1.nextLong();
1119 }
1120
1121 jump.apply(rng2);
1122 for (int j = 0; j < cycles; j++) {
1123 Assertions.assertEquals(rng1.nextLong(), rng2.nextLong(),
1124 () -> String.format("[%d] Incorrect after catch up jump, seed=%d", index, testSeed));
1125 }
1126
1127
1128 copy = rng1;
1129 rng1 = rng2;
1130 rng2 = copy;
1131 }
1132 }
1133
1134 @RepeatedTest(value = 5)
1135 final void testSaveRestoreAfterJump() {
1136 assertSaveRestoreAfterJump(false, ThreadLocalRandom.current().nextLong());
1137 }
1138
1139 @RepeatedTest(value = 5)
1140 final void testSaveRestoreAfterLongJump() {
1141 assertSaveRestoreAfterJump(true, ThreadLocalRandom.current().nextLong());
1142 }
1143
1144
1145
1146
1147
1148
1149
1150
1151 private void assertSaveRestoreAfterJump(boolean longJump, long testSeed) {
1152 final SplittableRandom r = new SplittableRandom(testSeed);
1153 final int cycles = getFactory().seedSize();
1154
1155 final UnaryOperator<LongJumpableUniformRandomProvider> jump = longJump ?
1156 x -> (LongJumpableUniformRandomProvider) x.longJump() :
1157 x -> (LongJumpableUniformRandomProvider) x.jump();
1158
1159
1160 final LongJumpableUniformRandomProvider rng1 = create(createRandomSeed(r::nextLong));
1161 final LongJumpableUniformRandomProvider rng2 = create(createRandomSeed(r::nextLong));
1162 jump.apply(rng1);
1163 jump.apply(rng2);
1164
1165
1166 RestorableUniformRandomProvider g1 = (RestorableUniformRandomProvider) rng1;
1167 RestorableUniformRandomProvider g2 = (RestorableUniformRandomProvider) rng2;
1168 g2.restoreState(g1.saveState());
1169
1170
1171 RandomAssert.assertNextLongEquals(cycles, rng1, rng2);
1172
1173
1174 jump.apply(rng1);
1175 jump.apply(rng2);
1176
1177 RandomAssert.assertNextLongEquals(cycles, rng1, rng2);
1178 }
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190 private long[] createExpectedSequence(long[] seed, int length, boolean longJump) {
1191 final LXMGenerator rng = getFactory().create(seed);
1192 if (longJump) {
1193 rng.longJump();
1194 } else {
1195 rng.jump();
1196 }
1197 return LongStream.generate(rng::nextLong).limit(length).toArray();
1198 }
1199
1200
1201
1202
1203
1204
1205 private long[] createRandomSeed() {
1206 return createRandomSeed(new SplittableRandom()::nextLong);
1207 }
1208
1209
1210
1211
1212
1213
1214
1215
1216 private long[] createRandomSeed(LongSupplier gen) {
1217 return LongStream.generate(gen).limit(getFactory().seedSize()).toArray();
1218 }
1219
1220
1221
1222
1223
1224
1225
1226
1227 private int[] createSteps(SplittableRandom rng) {
1228 return createSteps(rng, getFactory().seedSize());
1229 }
1230
1231
1232
1233
1234
1235
1236
1237
1238 private static int[] createSteps(SplittableRandom rng, int seedSize) {
1239 final int[] steps = IntStream.rangeClosed(1, seedSize).toArray();
1240
1241 for (int i = steps.length; i > 1; i--) {
1242 final int j = rng.nextInt(i);
1243 final int tmp = steps[i - 1];
1244 steps[i - 1] = steps[j];
1245 steps[j] = tmp;
1246 }
1247 return steps;
1248 }
1249 }