1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.rng.core;
18
19 import java.util.Arrays;
20 import java.util.List;
21 import java.util.ArrayList;
22 import java.util.concurrent.Callable;
23
24 import org.junit.Assert;
25 import org.junit.Test;
26 import org.junit.runner.RunWith;
27 import org.junit.runners.Parameterized;
28 import org.junit.runners.Parameterized.Parameters;
29
30 import org.apache.commons.rng.UniformRandomProvider;
31 import org.apache.commons.rng.RestorableUniformRandomProvider;
32 import org.apache.commons.rng.RandomProviderState;
33
34
35
36
37 @RunWith(value=Parameterized.class)
38 public class ProvidersCommonParametricTest {
39
40 private final RestorableUniformRandomProvider generator;
41
42
43
44
45
46
47 public ProvidersCommonParametricTest(RestorableUniformRandomProvider rng) {
48 generator = rng;
49 }
50
51 @Parameters(name = "{index}: data={0}")
52 public static Iterable<RestorableUniformRandomProvider[]> getList() {
53 return ProvidersList.list();
54 }
55
56
57
58
59 @Test(expected=IllegalArgumentException.class)
60 public void testPreconditionNextInt1() {
61 generator.nextInt(-1);
62 }
63
64 @Test(expected=IllegalArgumentException.class)
65 public void testPreconditionNextInt2() {
66 generator.nextInt(0);
67 }
68
69 @Test(expected=IllegalArgumentException.class)
70 public void testPreconditionNextLong1() {
71 generator.nextLong(-1);
72 }
73
74 @Test(expected=IllegalArgumentException.class)
75 public void testPreconditionNextLong2() {
76 generator.nextLong(0);
77 }
78
79 @Test(expected=IndexOutOfBoundsException.class)
80 public void testPreconditionNextBytes1() {
81 final int size = 10;
82 final int num = 1;
83 final byte[] buf = new byte[size];
84 generator.nextBytes(buf, -1, num);
85 }
86 @Test(expected=IndexOutOfBoundsException.class)
87 public void testPreconditionNextBytes2() {
88 final int size = 10;
89 final byte[] buf = new byte[size];
90 generator.nextBytes(buf, size, 0);
91 }
92 @Test(expected=IndexOutOfBoundsException.class)
93 public void testPreconditionNextBytes3() {
94 final int size = 10;
95 final int offset = 2;
96 final byte[] buf = new byte[size];
97 generator.nextBytes(buf, offset, size - offset + 1);
98 }
99 @Test(expected=IndexOutOfBoundsException.class)
100 public void testPreconditionNextBytes4() {
101 final int size = 10;
102 final int offset = 1;
103 final byte[] buf = new byte[size];
104 generator.nextBytes(buf, offset, -1);
105 }
106
107
108
109
110 @Test
111 public void testUniformNextBytesFullBuffer() {
112
113
114 final int size = 23;
115 final byte[] buffer = new byte[size];
116
117 final Runnable nextMethod = new Runnable() {
118 @Override
119 public void run() {
120 generator.nextBytes(buffer);
121 }
122 };
123
124 Assert.assertTrue(isUniformNextBytes(buffer, 0, size, nextMethod));
125 }
126
127 @Test
128 public void testUniformNextBytesPartialBuffer() {
129 final int totalSize = 1234;
130 final int offset = 567;
131 final int size = 89;
132
133 final byte[] buffer = new byte[totalSize];
134
135 final Runnable nextMethod = new Runnable() {
136 @Override
137 public void run() {
138 generator.nextBytes(buffer, offset, size);
139 }
140 };
141
142
143 Assert.assertTrue(isUniformNextBytes(buffer, offset, offset + size, nextMethod));
144
145
146 Assert.assertFalse(isUniformNextBytes(buffer, 0, offset, nextMethod));
147 Assert.assertFalse(isUniformNextBytes(buffer, offset + size, buffer.length, nextMethod));
148 }
149
150 @Test
151 public void testUniformNextIntegerInRange() {
152
153 checkNextIntegerInRange(10, 1000);
154 checkNextIntegerInRange(12, 1000);
155 checkNextIntegerInRange(31, 1000);
156 checkNextIntegerInRange(32, 1000);
157 checkNextIntegerInRange(2016128993, 1000);
158 checkNextIntegerInRange(1834691456, 1000);
159 checkNextIntegerInRange(869657561, 1000);
160 checkNextIntegerInRange(1570504788, 1000);
161 }
162
163 @Test
164 public void testUniformNextLongInRange() {
165
166 checkNextLongInRange(11, 1000);
167 checkNextLongInRange(19, 1000);
168 checkNextLongInRange(31, 1000);
169 checkNextLongInRange(32, 1000);
170
171 final long q = Long.MAX_VALUE / 4;
172 checkNextLongInRange(q, 1000);
173 checkNextLongInRange(2 * q, 1000);
174 checkNextLongInRange(3 * q, 1000);
175 }
176
177 @Test
178 public void testUniformNextFloat() {
179 checkNextFloat(1000);
180 }
181
182 @Test
183 public void testUniformNextDouble() {
184 checkNextDouble(1000);
185 }
186
187 @Test
188 public void testUniformNextIntRandomWalk() {
189 final Callable<Boolean> nextMethod = new Callable<Boolean>() {
190 @Override
191 public Boolean call() throws Exception {
192 return generator.nextInt() >= 0;
193 }
194 };
195
196 checkRandomWalk(1000, nextMethod);
197 }
198
199 @Test
200 public void testUniformNextLongRandomWalk() {
201 final Callable<Boolean> nextMethod = new Callable<Boolean>() {
202 @Override
203 public Boolean call() throws Exception {
204 return generator.nextLong() >= 0;
205 }
206 };
207
208 checkRandomWalk(1000, nextMethod);
209 }
210
211 @Test
212 public void testUniformNextBooleanRandomWalk() {
213 final Callable<Boolean> nextMethod = new Callable<Boolean>() {
214 @Override
215 public Boolean call() throws Exception {
216 return generator.nextBoolean();
217 }
218 };
219
220 checkRandomWalk(1000, nextMethod);
221 }
222
223
224
225 @Test
226 public void testStateSettable() {
227
228
229 final int n = 10000;
230
231
232 final RandomProviderState state = generator.saveState();
233
234 final List<Number> listOrig = makeList(n);
235
236 final List<Number> listDiscard = makeList(n);
237 Assert.assertTrue(listDiscard.size() != 0);
238 Assert.assertFalse(listOrig.equals(listDiscard));
239
240 generator.restoreState(state);
241
242 final List<Number> listReplay = makeList(n);
243 Assert.assertFalse(listOrig == listReplay);
244
245 Assert.assertTrue(listOrig.equals(listReplay));
246 }
247
248 @Test(expected=IllegalStateException.class)
249 public void testStateWrongSize() {
250 final RandomProviderState state = new DummyGenerator().saveState();
251
252 generator.restoreState(state);
253 }
254
255 @Test(expected=IllegalArgumentException.class)
256 public void testRestoreForeignState() {
257 generator.restoreState(new RandomProviderState() {});
258 }
259
260
261
262
263
264
265
266
267
268
269 private List<Number> makeList(int n) {
270 final List<Number> list = new ArrayList<Number>();
271
272 for (int i = 0; i < n; i++) {
273
274 list.add(generator.nextInt());
275 list.add(generator.nextInt(21));
276 list.add(generator.nextInt(436));
277 list.add(generator.nextLong());
278 list.add(generator.nextLong(157894));
279 list.add(generator.nextLong(5745833));
280 list.add(generator.nextFloat());
281 list.add(generator.nextFloat());
282 list.add(generator.nextDouble());
283 list.add(generator.nextDouble());
284 list.add(generator.nextBoolean() ? 1 : 0);
285 }
286
287 return list;
288 }
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304 private boolean isUniformNextBytes(byte[] buffer,
305 int first,
306 int last,
307 Runnable nextMethod) {
308 final int sampleSize = 10000;
309
310
311 final int byteRange = 256;
312
313
314 final double chi2CriticalValue = 311.560343;
315
316 final int byteRangeOffset = 128;
317
318
319 final long[] observed = new long[byteRange];
320 final double[] expected = new double[byteRange];
321
322 for (int i = 0; i < byteRange; i++) {
323 expected[i] = sampleSize * (last - first) / (double) byteRange;
324 }
325
326 try {
327 for (int k = 0; k < sampleSize; k++) {
328 nextMethod.run();
329
330 for (int i = first; i < last; i++) {
331 final byte b = buffer[i];
332 ++observed[b + byteRangeOffset];
333 }
334 }
335 } catch (Exception e) {
336
337 throw new RuntimeException("Unexpected");
338 }
339
340
341 double chi2 = 0;
342 for (int k = 0; k < byteRange; k++) {
343 final double diff = observed[k] - expected[k];
344 chi2 += diff * diff / expected[k];
345 }
346
347
348 return chi2 < chi2CriticalValue;
349 }
350
351
352
353
354
355
356
357
358
359
360 private void checkRandomWalk(int sampleSize,
361 Callable<Boolean> nextMethod) {
362 int walk = 0;
363
364 try {
365 for (int k = 0; k < sampleSize; ++k) {
366 if (nextMethod.call()) {
367 ++walk;
368 } else {
369 --walk;
370 }
371 }
372 } catch (Exception e) {
373
374 throw new RuntimeException("Unexpected");
375 }
376
377 final double actual = Math.abs(walk);
378 final double max = Math.sqrt(sampleSize) * 2.576;
379 Assert.assertTrue(generator + ": Walked too far astray: " + actual +
380 " > " + max +
381 " (test will fail randomly about 1 in 100 times)",
382 actual < max);
383 }
384
385
386
387
388
389
390
391 private void checkNextIntegerInRange(final int max,
392 int sampleSize) {
393 checkNextIntegerInRange(generator, max, sampleSize);
394 }
395
396
397
398
399
400
401
402
403 private void checkNextIntegerInRange(final UniformRandomProvider rng,
404 final int max,
405 int sampleSize) {
406 final Callable<Integer> nextMethod = new Callable<Integer>() {
407 @Override
408 public Integer call() throws Exception {
409 return rng.nextInt(max);
410 }
411 };
412
413 checkNextInRange(max, sampleSize, nextMethod);
414 }
415
416
417
418
419
420
421
422 private void checkNextLongInRange(final long max,
423 int sampleSize) {
424 final Callable<Long> nextMethod = new Callable<Long>() {
425 @Override
426 public Long call() throws Exception {
427 return generator.nextLong(max);
428 }
429 };
430
431 checkNextInRange(max, sampleSize, nextMethod);
432 }
433
434
435
436
437
438
439 private void checkNextFloat(int sampleSize) {
440 final int max = 1234;
441 final Callable<Integer> nextMethod = new Callable<Integer>() {
442 @Override
443 public Integer call() throws Exception {
444 return (int) (max * generator.nextFloat());
445 }
446 };
447
448 checkNextInRange(max, sampleSize, nextMethod);
449 }
450
451
452
453
454
455
456 private void checkNextDouble(int sampleSize) {
457 final int max = 578;
458 final Callable<Integer> nextMethod = new Callable<Integer>() {
459 @Override
460 public Integer call() throws Exception {
461 return (int) (max * generator.nextDouble());
462 }
463 };
464
465 checkNextInRange(max, sampleSize, nextMethod);
466 }
467
468
469
470
471
472
473
474
475
476
477
478
479
480 private <T extends Number> void checkNextInRange(T max,
481 int sampleSize,
482 Callable<T> nextMethod) {
483 final int numTests = 500;
484
485
486 final int numBins = 10;
487
488
489 final long n = max.longValue();
490 final long[] binUpperBounds = new long[numBins];
491 final double step = n / (double) numBins;
492 for (int k = 0; k < numBins; k++) {
493 binUpperBounds[k] = (long) ((k + 1) * step);
494 }
495
496 binUpperBounds[numBins - 1] = n;
497
498
499 int numFailures = 0;
500
501 final double[] expected = new double[numBins];
502 long previousUpperBound = 0;
503 for (int k = 0; k < numBins; k++) {
504 final long range = binUpperBounds[k] - previousUpperBound;
505 expected[k] = sampleSize * (range / (double) n);
506 previousUpperBound = binUpperBounds[k];
507 }
508
509 final int[] observed = new int[numBins];
510
511
512 final double chi2CriticalValue = 21.67;
513
514 try {
515 for (int i = 0; i < numTests; i++) {
516 Arrays.fill(observed, 0);
517 for (int j = 0; j < sampleSize; j++) {
518 final long value = nextMethod.call().longValue();
519 Assert.assertTrue("Range", (value >= 0) && (value < n));
520
521 for (int k = 0; k < numBins; k++) {
522 if (value < binUpperBounds[k]) {
523 ++observed[k];
524 break;
525 }
526 }
527 }
528
529
530 double chi2 = 0;
531 for (int k = 0; k < numBins; k++) {
532 final double diff = observed[k] - expected[k];
533 chi2 += diff * diff / expected[k];
534 }
535
536
537 if (chi2 > chi2CriticalValue) {
538 ++numFailures;
539 }
540 }
541 } catch (Exception e) {
542
543 throw new RuntimeException("Unexpected", e);
544 }
545
546
547
548
549
550
551
552
553
554 if (numFailures > 11) {
555 Assert.fail(generator + ": Too many failures for n = " + n +
556 " (" + numFailures + " out of " + numTests + " tests failed)");
557 }
558 }
559
560
561
562
563
564
565 static void checkNextBytesChunks(RestorableUniformRandomProvider rng,
566 int chunkSize,
567 int numChunks) {
568 final byte[] b1 = new byte[chunkSize * numChunks];
569 final byte[] b2 = new byte[chunkSize];
570
571 final RandomProviderState state = rng.saveState();
572
573
574 rng.nextBytes(b1);
575
576
577 rng.restoreState(state);
578
579
580 for (int i = 0; i < numChunks; i++) {
581 rng.nextBytes(b2);
582 }
583
584
585 final byte[] b3 = new byte[chunkSize];
586 System.arraycopy(b1, b1.length - b3.length, b3, 0, b3.length);
587
588
589 Assert.assertArrayEquals("chunkSize=" + chunkSize + " numChunks=" + numChunks,
590 b2, b3);
591 }
592
593
594
595
596 class DummyGenerator extends org.apache.commons.rng.core.source32.IntProvider {
597
598 @Override
599 public int next() {
600 return 4;
601 }
602
603
604 @Override
605 protected byte[] getStateInternal() {
606 return new byte[0];
607 }
608
609
610 @Override
611 protected void setStateInternal(byte[] s) {
612
613 }
614 }
615 }