1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.rng.simple;
18
19 import java.util.Arrays;
20 import java.util.List;
21 import java.util.ArrayList;
22 import java.util.function.LongSupplier;
23 import java.util.stream.Collectors;
24 import java.io.IOException;
25 import java.io.ObjectOutputStream;
26 import java.io.ObjectInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.ByteArrayInputStream;
29
30 import org.junit.jupiter.api.Assertions;
31 import org.junit.jupiter.api.Assumptions;
32 import org.junit.jupiter.params.ParameterizedTest;
33 import org.junit.jupiter.params.provider.MethodSource;
34
35 import org.apache.commons.rng.UniformRandomProvider;
36 import org.apache.commons.rng.JumpableUniformRandomProvider;
37 import org.apache.commons.rng.LongJumpableUniformRandomProvider;
38 import org.apache.commons.rng.RandomProviderState;
39 import org.apache.commons.rng.RestorableUniformRandomProvider;
40 import org.apache.commons.rng.SplittableUniformRandomProvider;
41 import org.apache.commons.rng.core.RandomProviderDefaultState;
42 import org.apache.commons.rng.core.source64.LongProvider;
43 import org.apache.commons.rng.core.source64.SplitMix64;
44
45
46
47
48 class ProvidersCommonParametricTest {
49 private static Iterable<ProvidersList.Data> getProvidersTestData() {
50 return ProvidersList.list();
51 }
52
53
54
55 @ParameterizedTest
56 @MethodSource("getProvidersTestData")
57 void testUnsupportedSeedType(ProvidersList.Data data) {
58 final RandomSource originalSource = data.getSource();
59 final byte seed = 123;
60 final Object[] originalArgs = data.getArgs();
61 Assertions.assertThrows(UnsupportedOperationException.class,
62 () -> originalSource.create(seed, originalArgs));
63 }
64
65
66
67
68 @ParameterizedTest
69 @MethodSource("getProvidersTestData")
70 void testFactoryCreateMethod(ProvidersList.Data data) {
71 final RandomSource originalSource = data.getSource();
72 final Object originalSeed = data.getSeed();
73 final Object[] originalArgs = data.getArgs();
74
75 Assumptions.assumeTrue(originalArgs == null);
76 @SuppressWarnings("deprecation")
77 final UniformRandomProvider rng = RandomSource.create(data.getSource());
78 final UniformRandomProvider generator = originalSource.create(originalSeed, originalArgs);
79 Assertions.assertEquals(generator.getClass(), rng.getClass());
80 }
81
82
83
84
85
86 @ParameterizedTest
87 @MethodSource("getProvidersTestData")
88 void testFactoryCreateMethodWithSeed(ProvidersList.Data data) {
89 final RandomSource originalSource = data.getSource();
90 final Object originalSeed = data.getSeed();
91 final Object[] originalArgs = data.getArgs();
92 final UniformRandomProvider generator = originalSource.create(originalSeed, originalArgs);
93 @SuppressWarnings("deprecation")
94 final UniformRandomProvider rng1 = RandomSource.create(originalSource, originalSeed, originalArgs);
95 Assertions.assertEquals(rng1.getClass(), generator.getClass());
96
97 final UniformRandomProvider rng2 = originalSource.create(originalSeed, originalArgs);
98 for (int i = 0; i < 10; i++) {
99 Assertions.assertEquals(rng2.nextLong(), rng1.nextLong());
100 }
101 }
102
103
104
105
106
107 @ParameterizedTest
108 @MethodSource("getProvidersTestData")
109 void testCreateMethodThrowsWithIncorrectArguments(ProvidersList.Data data) {
110 final RandomSource originalSource = data.getSource();
111 final Object[] originalArgs = data.getArgs();
112 if (originalArgs == null) {
113
114 final int arg1 = 123;
115 final double arg2 = 456.0;
116 Assertions.assertThrows(IllegalArgumentException.class,
117 () -> originalSource.create(arg1, arg2),
118 () -> "Source does not require arguments: " + originalSource);
119 } else {
120
121 Assertions.assertThrows(IllegalArgumentException.class,
122 () -> originalSource.create(),
123 () -> "Source requires arguments: " + originalSource);
124 }
125 }
126
127 @ParameterizedTest
128 @MethodSource("getProvidersTestData")
129 void testAllSeedTypes(ProvidersList.Data data) {
130 final RandomSource originalSource = data.getSource();
131 final Object originalSeed = data.getSeed();
132 final Object[] originalArgs = data.getArgs();
133 final Integer intSeed = -12131415;
134 final Long longSeed = -1213141516171819L;
135 final int[] intArraySeed = new int[] {0, 11, -22, 33, -44, 55, -66, 77, -88, 99};
136 final long[] longArraySeed = new long[] {11111L, -222222L, 3333333L, -44444444L};
137 final byte[] byteArraySeed = new byte[] {-128, -91, -45, -32, -1, 0, 11, 23, 54, 88, 127};
138
139 final Object[] seeds = new Object[] {null,
140 intSeed,
141 longSeed,
142 intArraySeed,
143 longArraySeed,
144 byteArraySeed};
145
146 int nonNativeSeedCount = 0;
147 int seedCount = 0;
148 for (final Object s : seeds) {
149 ++seedCount;
150 if (originalSource.isNativeSeed(s)) {
151 Assertions.assertNotNull(s, "Identified native seed is null");
152 Assertions.assertEquals(s.getClass(), originalSeed.getClass(),
153 "Incorrect identification of native seed type");
154 } else {
155 ++nonNativeSeedCount;
156 }
157
158 originalSource.create(s, originalArgs);
159 }
160
161 Assertions.assertEquals(6, seedCount);
162 Assertions.assertEquals(5, nonNativeSeedCount);
163 }
164
165 @ParameterizedTest
166 @MethodSource("getProvidersTestData")
167 void testNullSeed(ProvidersList.Data data) {
168 final RandomSource originalSource = data.getSource();
169 final Object[] originalArgs = data.getArgs();
170
171 final UniformRandomProvider rng = originalArgs == null ?
172 originalSource.create() :
173 originalSource.create(null, originalArgs);
174 checkNextIntegerInRange(rng, 10, 10000);
175 }
176
177 @ParameterizedTest
178 @MethodSource("getProvidersTestData")
179 void testEmptyIntArraySeed(ProvidersList.Data data) {
180 final RandomSource originalSource = data.getSource();
181 final Object[] originalArgs = data.getArgs();
182 final int[] empty = new int[0];
183 Assumptions.assumeTrue(originalSource.isNativeSeed(empty));
184
185
186 final UniformRandomProvider rng = originalSource.create(empty, originalArgs);
187 checkNextIntegerInRange(rng, 10, 20000);
188 }
189
190 @ParameterizedTest
191 @MethodSource("getProvidersTestData")
192 void testEmptyLongArraySeed(ProvidersList.Data data) {
193 final RandomSource originalSource = data.getSource();
194 final Object[] originalArgs = data.getArgs();
195 final long[] empty = new long[0];
196 Assumptions.assumeTrue(originalSource.isNativeSeed(empty));
197
198
199 final UniformRandomProvider rng = originalSource.create(empty, originalArgs);
200 checkNextIntegerInRange(rng, 10, 10000);
201 }
202
203 @ParameterizedTest
204 @MethodSource("getProvidersTestData")
205 void testZeroIntArraySeed(ProvidersList.Data data) {
206 final RandomSource originalSource = data.getSource();
207 final Object[] originalArgs = data.getArgs();
208
209 final int[] zero = new int[2000];
210 final UniformRandomProvider rng = originalSource.create(zero, originalArgs);
211 Assumptions.assumeTrue(createsNonZeroLongOutput(rng, 2000),
212 () -> "RNG is non-functional with an all zero seed: " + originalSource);
213 checkNextIntegerInRange(rng, 10, 10000);
214 }
215
216 @ParameterizedTest
217 @MethodSource("getProvidersTestData")
218 void testZeroLongArraySeed(ProvidersList.Data data) {
219 final RandomSource originalSource = data.getSource();
220 final Object[] originalArgs = data.getArgs();
221
222 final long[] zero = new long[2000];
223 final UniformRandomProvider rng = originalSource.create(zero, originalArgs);
224 Assumptions.assumeTrue(createsNonZeroLongOutput(rng, 2000),
225 () -> "RNG is non-functional with an all zero seed: " + originalSource);
226 checkNextIntegerInRange(rng, 10, 10000);
227 }
228
229 @ParameterizedTest
230 @MethodSource("getProvidersTestData")
231 void testRandomSourceCreateSeed(ProvidersList.Data data) {
232 final RandomSource originalSource = data.getSource();
233 final Object[] originalArgs = data.getArgs();
234 final byte[] seed = originalSource.createSeed();
235 final UniformRandomProvider rng = originalSource.create(seed, originalArgs);
236 checkNextIntegerInRange(rng, 10, 10000);
237 }
238
239 @ParameterizedTest
240 @MethodSource("getProvidersTestData")
241 void testRandomSourceCreateSeedFromRNG(ProvidersList.Data data) {
242 final RandomSource originalSource = data.getSource();
243 final Object[] originalArgs = data.getArgs();
244 final byte[] seed = originalSource.createSeed(new SplitMix64(RandomSource.createLong()));
245 final UniformRandomProvider rng = originalSource.create(seed, originalArgs);
246 checkNextIntegerInRange(rng, 10, 10000);
247 }
248
249 @ParameterizedTest
250 @MethodSource("getProvidersTestData")
251 void testRandomSourceCreateSeedFromInvalidRNG(ProvidersList.Data data) {
252
253
254
255 final LongProvider badRng = new LongProvider() {
256 @Override
257 public long next() {
258 return 0;
259 }
260 };
261
262 final RandomSource originalSource = data.getSource();
263 final byte[] seed = originalSource.createSeed(badRng);
264
265
266 Assertions.assertArrayEquals(seed, originalSource.createSeed(badRng));
267
268
269 final Object[] originalArgs = data.getArgs();
270 final UniformRandomProvider rng = originalSource.create(seed, originalArgs);
271 checkNextIntegerInRange(rng, 10, 10000);
272 }
273
274
275
276 @SuppressWarnings("unused")
277 @ParameterizedTest
278 @MethodSource("getProvidersTestData")
279 void testUnrestorable(ProvidersList.Data data) {
280 final RandomSource originalSource = data.getSource();
281 final Object originalSeed = data.getSeed();
282 final Object[] originalArgs = data.getArgs();
283
284 final UniformRandomProvider rng1 = originalSource.create(originalSeed, originalArgs);
285 final UniformRandomProvider rng2 = RandomSource.unrestorable(originalSource.create(originalSeed, originalArgs));
286
287
288 RandomAssert.assertProduceSameSequence(rng1, rng2);
289
290
291 final RestorableUniformRandomProvider restorable = (RestorableUniformRandomProvider) rng1;
292
293 Assertions.assertThrows(ClassCastException.class, () -> {
294 final RestorableUniformRandomProvider dummy = (RestorableUniformRandomProvider) rng2;
295 });
296 }
297
298 @ParameterizedTest
299 @MethodSource("getProvidersTestData")
300 void testSerializingState(ProvidersList.Data data)
301 throws IOException,
302 ClassNotFoundException {
303 final UniformRandomProvider generator = data.getSource().create(data.getSeed(), data.getArgs());
304
305
306 final int n = 100;
307
308
309 final RestorableUniformRandomProvider restorable = (RestorableUniformRandomProvider) generator;
310
311
312 final RandomProviderState stateOrig = restorable.saveState();
313
314 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
315 final ObjectOutputStream oos = new ObjectOutputStream(bos);
316 oos.writeObject(((RandomProviderDefaultState) stateOrig).getState());
317
318
319 final List<Number> listOrig = makeList(n, generator);
320
321
322 final List<Number> listDiscard = makeList(n, generator);
323 Assertions.assertNotEquals(0, listDiscard.size());
324 Assertions.assertNotEquals(listOrig, listDiscard);
325
326
327 final ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
328 final ObjectInputStream ois = new ObjectInputStream(bis);
329 final RandomProviderState stateNew = new RandomProviderDefaultState((byte[]) ois.readObject());
330
331 Assertions.assertNotSame(stateOrig, stateNew);
332
333
334 restorable.restoreState(stateNew);
335
336
337 final List<Number> listReplay = makeList(n, generator);
338 Assertions.assertNotSame(listOrig, listReplay);
339
340
341 Assertions.assertEquals(listOrig, listReplay);
342 }
343
344 @ParameterizedTest
345 @MethodSource("getProvidersTestData")
346 void testUnrestorableToString(ProvidersList.Data data) {
347 final UniformRandomProvider generator = data.getSource().create(data.getSeed(), data.getArgs());
348 Assertions.assertEquals(generator.toString(),
349 RandomSource.unrestorable(generator).toString());
350 }
351
352 @ParameterizedTest
353 @MethodSource("getProvidersTestData")
354 void testSupportedInterfaces(ProvidersList.Data data) {
355 final RandomSource originalSource = data.getSource();
356 final Object[] originalArgs = data.getArgs();
357 final UniformRandomProvider rng = originalSource.create(null, originalArgs);
358 Assertions.assertEquals(rng instanceof JumpableUniformRandomProvider,
359 originalSource.isJumpable(),
360 "isJumpable");
361 Assertions.assertEquals(rng instanceof LongJumpableUniformRandomProvider,
362 originalSource.isLongJumpable(),
363 "isLongJumpable");
364 Assertions.assertEquals(rng instanceof SplittableUniformRandomProvider,
365 originalSource.isSplittable(),
366 "isSplittable");
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386 private static List<Number> makeList(int n, UniformRandomProvider generator) {
387 final List<Number> list = new ArrayList<>();
388
389 for (int i = 0; i < n; i++) {
390
391 list.add(generator.nextInt());
392 list.add(generator.nextInt(21));
393 list.add(generator.nextInt(436));
394 list.add(generator.nextLong());
395 list.add(generator.nextLong(157894));
396 list.add(generator.nextLong(5745833));
397 list.add(generator.nextFloat());
398 list.add(generator.nextFloat());
399 list.add(generator.nextDouble());
400 list.add(generator.nextDouble());
401 list.add(generator.nextBoolean() ? 1 : 0);
402 }
403
404 return list;
405 }
406
407
408
409
410
411
412
413
414
415 private static void checkNextIntegerInRange(final UniformRandomProvider rng,
416 final int max,
417 int sampleSize) {
418 final LongSupplier nextMethod = () -> rng.nextInt(max);
419
420 checkNextInRange(rng, max, sampleSize, nextMethod);
421 }
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436 private static void checkNextInRange(UniformRandomProvider generator,
437 long n,
438 int sampleSize,
439 LongSupplier nextMethod) {
440 final int numTests = 500;
441
442
443 final int numBins = 10;
444
445
446 final long[] binUpperBounds = new long[numBins];
447 final double step = n / (double) numBins;
448 for (int k = 0; k < numBins; k++) {
449 binUpperBounds[k] = (long) ((k + 1) * step);
450 }
451
452 binUpperBounds[numBins - 1] = n;
453
454
455 int numFailures = 0;
456
457 final double[] expected = new double[numBins];
458 long previousUpperBound = 0;
459 for (int k = 0; k < numBins; k++) {
460 final long range = binUpperBounds[k] - previousUpperBound;
461 expected[k] = sampleSize * (range / (double) n);
462 previousUpperBound = binUpperBounds[k];
463 }
464
465 final int[] observed = new int[numBins];
466
467
468 final double chi2CriticalValue = 21.665994333461924;
469
470
471 final List<Double> failedStat = new ArrayList<>();
472 try {
473 final int lastDecileIndex = numBins - 1;
474 for (int i = 0; i < numTests; i++) {
475 Arrays.fill(observed, 0);
476 SAMPLE: for (int j = 0; j < sampleSize; j++) {
477 final long value = nextMethod.getAsLong();
478 Assertions.assertTrue(value >= 0 && value < n, "Range");
479
480 for (int k = 0; k < lastDecileIndex; k++) {
481 if (value < binUpperBounds[k]) {
482 ++observed[k];
483 continue SAMPLE;
484 }
485 }
486 ++observed[lastDecileIndex];
487 }
488
489
490 double chi2 = 0;
491 for (int k = 0; k < numBins; k++) {
492 final double diff = observed[k] - expected[k];
493 chi2 += diff * diff / expected[k];
494 }
495
496
497 if (chi2 > chi2CriticalValue) {
498 failedStat.add(chi2);
499 ++numFailures;
500 }
501 }
502 } catch (final Exception e) {
503
504 throw new RuntimeException("Unexpected", e);
505 }
506
507
508
509
510
511
512
513
514
515 if (numFailures > 11) {
516 Assertions.fail(String.format(
517 "%s: Too many failures for n = %d, sample size = %d " +
518 "(%d out of %d tests failed, chi2 > %.3f=%s)",
519 generator, n, sampleSize, numFailures, numTests, chi2CriticalValue,
520 failedStat.stream().map(d -> String.format("%.3f", d))
521 .collect(Collectors.joining(", ", "[", "]"))));
522 }
523 }
524
525
526
527
528
529
530
531
532
533 private static boolean createsNonZeroLongOutput(UniformRandomProvider rng,
534 int cycles) {
535 boolean nonZero = false;
536 for (int i = 0; i < cycles; i++) {
537 if (rng.nextLong() != 0) {
538 nonZero = true;
539 }
540 }
541 return nonZero;
542 }
543 }