1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.rng.simple.internal;
18
19 import java.util.Map;
20 import java.util.Arrays;
21 import java.util.HashMap;
22 import org.junit.jupiter.api.Assertions;
23 import org.junit.jupiter.api.Test;
24 import org.junit.jupiter.params.ParameterizedTest;
25 import org.junit.jupiter.params.provider.CsvSource;
26 import org.junit.jupiter.params.provider.ValueSource;
27 import org.apache.commons.rng.UniformRandomProvider;
28 import org.apache.commons.rng.core.source32.IntProvider;
29 import org.apache.commons.rng.core.source64.LongProvider;
30 import org.apache.commons.rng.core.source64.RandomLongSource;
31 import org.apache.commons.rng.core.source64.SplitMix64;
32 import org.apache.commons.rng.core.util.NumberFactory;
33
34
35
36
37 class SeedFactoryTest {
38 @Test
39 void testCreateLong() {
40 final Map<Long, Integer> values = new HashMap<>();
41
42 final int n = 100000;
43 for (int i = 0; i < n; i++) {
44 final long v = SeedFactory.createLong();
45
46 Integer count = values.get(v);
47 if (count == null) {
48 count = 0;
49 }
50 values.put(v, count + 1);
51 }
52
53
54 assertDifferentValues(values);
55 }
56
57 @Test
58 void testCreateLongArray() {
59 final Map<Long, Integer> values = new HashMap<>();
60
61 final int n = 100000;
62 final long[] array = SeedFactory.createLongArray(n);
63 Assertions.assertEquals(n, array.length);
64
65 for (long v : array) {
66 Integer count = values.get(v);
67 if (count == null) {
68 count = 0;
69 }
70 values.put(v, count + 1);
71 }
72
73
74 assertDifferentValues(values);
75 }
76
77 @Test
78 void testCreateIntArray() {
79 final Map<Long, Integer> values = new HashMap<>();
80
81 for (int i = 0; i < 50000; i++) {
82 final int[] a = SeedFactory.createIntArray(2);
83 final long v = NumberFactory.makeLong(a[0], a[1]);
84 Integer count = values.get(v);
85 if (count == null) {
86 count = 0;
87 }
88 values.put(v, count + 1);
89 }
90
91
92 assertDifferentValues(values);
93 }
94
95
96
97
98
99
100
101 private static <T> void assertDifferentValues(Map<T, Integer> map) {
102 final StringBuilder sb = new StringBuilder();
103
104 int duplicates = 0;
105 for (Map.Entry<T, Integer> entry : map.entrySet()) {
106 final int count = entry.getValue();
107 if (count <= 0) {
108 throw new IllegalStateException();
109 }
110
111 if (count > 1) {
112 duplicates += count - 1;
113 sb.append(entry.getKey() + ": " + count + "\n");
114 }
115 }
116
117 if (duplicates > 0) {
118 Assertions.fail(duplicates + " duplicates\n" + sb);
119 }
120 }
121
122 @Test
123 void testCreateIntArrayWithZeroSize() {
124 Assertions.assertArrayEquals(new int[0], SeedFactory.createIntArray(0));
125 }
126
127 @Test
128 void testCreateIntArrayWithCompleteBlockSize() {
129
130 assertCreateIntArray(8);
131 }
132
133 @Test
134 void testCreateIntArrayWithIncompleteBlockSize() {
135
136 assertCreateIntArray(8 + 1);
137 }
138
139
140
141
142
143
144
145
146 private static void assertCreateIntArray(int n) {
147 final int[] array = SeedFactory.createIntArray(n);
148 Assertions.assertEquals(n, array.length, "Incorrect array length");
149
150 int bitCount = 0;
151 for (final int i : array) {
152 bitCount += Integer.bitCount(i);
153 }
154 final int numberOfBits = n * Integer.SIZE;
155 assertMonobit(bitCount, numberOfBits);
156 }
157
158 @Test
159 void testCreateLongArrayWithZeroSize() {
160 Assertions.assertArrayEquals(new long[0], SeedFactory.createLongArray(0));
161 }
162
163 @Test
164 void testCreateLongArrayWithCompleteBlockSize() {
165
166 assertCreateLongArray(4);
167 }
168
169 @Test
170 void testCreateLongArrayWithIncompleteBlockSize() {
171
172 assertCreateLongArray(4 + 1);
173 }
174
175
176
177
178
179
180
181
182 private static void assertCreateLongArray(int n) {
183 final long[] array = SeedFactory.createLongArray(n);
184 Assertions.assertEquals(n, array.length, "Incorrect array length");
185
186 int bitCount = 0;
187 for (final long i : array) {
188 bitCount += Long.bitCount(i);
189 }
190 final int numberOfBits = n * Long.SIZE;
191 assertMonobit(bitCount, numberOfBits);
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 private static void assertMonobit(int bitCount, int numberOfBits) {
210
211 final double sum = 2.0 * bitCount - numberOfBits;
212
213
214
215
216
217
218 final double absSum = Math.abs(sum);
219 final double max = Math.sqrt(numberOfBits) * 3.891;
220 Assertions.assertTrue(absSum <= max,
221 () -> "Walked too far astray: " + absSum +
222 " > " + max +
223 " (test will fail randomly about 1 in 10,000 times)");
224 }
225
226 @Test
227 void testCreateByteArrayWithSizeZero() {
228 assertCreateByteArray(new byte[0]);
229 }
230
231 @Test
232 void testCreateByteArrayIgnoresNonZeroPositions() {
233 final byte position = 123;
234 int n = 3;
235 for (int i = 0; i < n; i++) {
236 final byte[] expected = new byte[n];
237 expected[i] = position;
238 assertCreateByteArray(expected);
239 }
240 }
241
242
243
244
245
246
247
248 private static void assertCreateByteArray(final byte[] expected) {
249 final UniformRandomProvider rng = new IntProvider() {
250 @Override
251 public int next() {
252 Assertions.fail("This method should not be used");
253 return 0;
254 }
255
256 @Override
257 public void nextBytes(byte[] bytes) {
258 System.arraycopy(expected, 0, bytes, 0, Math.min(expected.length, bytes.length));
259 }
260 };
261
262 final byte[] seed = SeedFactory.createByteArray(rng, expected.length, 0, expected.length);
263 Assertions.assertArrayEquals(expected, seed);
264 }
265
266 @Test
267 void testCreateByteArrayWithAllZeroBytesUpdatesFromTo() {
268 final UniformRandomProvider rng = new IntProvider() {
269 @Override
270 public int next() {
271
272 return 0;
273 }
274 };
275
276
277 final int size = 4;
278 for (int start = 0; start < size; start++) {
279 final int from = start;
280 for (int end = start; end < size; end++) {
281 final int to = end;
282 final byte[] seed = SeedFactory.createByteArray(rng, 4, from, to);
283
284
285 for (int i = 0; i < from; i++) {
286 final int index = i;
287 Assertions.assertEquals(0, seed[i],
288 () -> String.format("[%d, %d) zero at position %d should not be modified",
289 from, to, index));
290 }
291 if (to > from) {
292 byte allBits = 0;
293 for (int i = from; i < to; i++) {
294 allBits |= seed[i];
295 }
296 Assertions.assertNotEquals(0, allBits,
297 () -> String.format("[%d, %d) should not be all zero", from, to));
298 }
299 for (int i = to; i < size; i++) {
300 final int index = i;
301 Assertions.assertEquals(0, seed[i],
302 () -> String.format("[%d, %d) zero at position %d should not be modified",
303 from, to, index));
304 }
305 }
306 }
307 }
308
309 @Test
310 void testEnsureNonZeroIntArrayIgnoresEmptySeed() {
311 final int[] seed = new int[0];
312 SeedFactory.ensureNonZero(seed, 0, 0);
313
314
315 }
316
317 @ParameterizedTest
318 @CsvSource({
319 "0, 0, 1",
320 "1, -1, 1",
321 "1, 0, 2",
322 "1, 1, 2",
323 })
324 void testEnsureNonZeroIntArrayThrowsWithInvalidRange(int n, int from, int to) {
325 final int[] seed = new int[n];
326 Assertions.assertThrows(IndexOutOfBoundsException.class,
327 () -> SeedFactory.ensureNonZero(seed, from, to));
328 }
329
330
331 @Test
332 void testEnsureNonZeroIntArrayWithAllZeroBytesUpdatesFromTo() {
333
334
335 final int size = 4;
336 final int[] seed = new int[size];
337 for (int start = 0; start < size; start++) {
338 final int from = start;
339 for (int end = start; end < size; end++) {
340 final int to = end;
341 Arrays.fill(seed, 0);
342 SeedFactory.ensureNonZero(seed, from, to);
343
344
345 for (int i = 0; i < from; i++) {
346 final int index = i;
347 Assertions.assertEquals(0, seed[i],
348 () -> String.format("[%d, %d) zero at position %d should not be modified",
349 from, to, index));
350 }
351 if (to > from) {
352 int allBits = 0;
353 for (int i = from; i < to; i++) {
354 allBits |= seed[i];
355 }
356 Assertions.assertNotEquals(0, allBits,
357 () -> String.format("[%d, %d) should not be all zero", from, to));
358 }
359 for (int i = to; i < size; i++) {
360 final int index = i;
361 Assertions.assertEquals(0, seed[i],
362 () -> String.format("[%d, %d) zero at position %d should not be modified",
363 from, to, index));
364 }
365 }
366 }
367 }
368
369 @Test
370 void testEnsureNonZeroLongArrayIgnoresEmptySeed() {
371 final long[] seed = new long[0];
372 SeedFactory.ensureNonZero(seed, 0, 0);
373
374
375 }
376
377 @ParameterizedTest
378 @CsvSource({
379 "0, 0, 1",
380 "1, -1, 1",
381 "1, 0, 2",
382 "1, 1, 2",
383 })
384 void testEnsureNonZeroLongArrayThrowsWithInvalidRange(int n, int from, int to) {
385 final long[] seed = new long[n];
386 Assertions.assertThrows(IndexOutOfBoundsException.class,
387 () -> SeedFactory.ensureNonZero(seed, from, to));
388 }
389
390
391 @Test
392 void testEnsureNonZeroLongArrayWithAllZeroBytesUpdatesFromTo() {
393
394
395 final int size = 4;
396 final long[] seed = new long[size];
397 for (int start = 0; start < size; start++) {
398 final int from = start;
399 for (int end = start; end < size; end++) {
400 final int to = end;
401 Arrays.fill(seed, 0);
402 SeedFactory.ensureNonZero(seed, from, to);
403
404
405 for (int i = 0; i < from; i++) {
406 final int index = i;
407 Assertions.assertEquals(0, seed[i],
408 () -> String.format("[%d, %d) zero at position %d should not be modified",
409 from, to, index));
410 }
411 if (to > from) {
412 long allBits = 0;
413 for (int i = from; i < to; i++) {
414 allBits |= seed[i];
415 }
416 Assertions.assertNotEquals(0, allBits,
417 () -> String.format("[%d, %d) should not be all zero", from, to));
418 }
419 for (int i = to; i < size; i++) {
420 final int index = i;
421 Assertions.assertEquals(0, seed[i],
422 () -> String.format("[%d, %d) zero at position %d should not be modified",
423 from, to, index));
424 }
425 }
426 }
427 }
428
429 @Test
430 void testEnsureNonZeroByteArrayIgnoresEmptySeed() {
431 final byte[] seed = new byte[0];
432 final UniformRandomProvider rng = new SplitMix64(123);
433 SeedFactory.ensureNonZero(seed, 0, 0, rng);
434
435
436 }
437
438 @ParameterizedTest
439 @CsvSource({
440 "0, 0, 1",
441 "1, -1, 1",
442 "1, 0, 2",
443 "1, 1, 2",
444 })
445 void testEnsureNonZeroByteArrayThrowsWithInvalidRange(int n, int from, int to) {
446 final byte[] seed = new byte[n];
447 final UniformRandomProvider rng = new SplitMix64(123);
448 Assertions.assertThrows(IndexOutOfBoundsException.class,
449 () -> SeedFactory.ensureNonZero(seed, from, to, rng));
450 }
451
452
453
454
455
456 @Test
457 void testZeroByteArraySourceOfRandomness() {
458 Assertions.assertEquals(0, MixFunctions.stafford13(-MixFunctions.GOLDEN_RATIO_64 + MixFunctions.GOLDEN_RATIO_64));
459 Assertions.assertEquals(0, MixFunctions.stafford13((1463436497261722119L << 1) + MixFunctions.GOLDEN_RATIO_64) & (-1L >>> 56));
460 Assertions.assertEquals(0, MixFunctions.stafford13((4949471497809997598L << 1) + MixFunctions.GOLDEN_RATIO_64) & (-1L >>> 48));
461
462
463
464
465 Assertions.assertEquals(0, MixFunctions.stafford13((953042962641938212L << 1) + MixFunctions.GOLDEN_RATIO_64) & (-1L >>> 8));
466 }
467
468 @ParameterizedTest
469 @ValueSource(longs = {0, -MixFunctions.GOLDEN_RATIO_64, 1463436497261722119L, 4949471497809997598L, 953042962641938212L})
470 void testEnsureNonZeroByteArrayWithAllZeroBytesUpdatesFromTo(long next) {
471
472
473 int[] calls = {0};
474 final UniformRandomProvider rng = new LongProvider() {
475 @Override
476 public long next() {
477 calls[0]++;
478 return next;
479 }
480 };
481
482
483
484 final int size = 2 * Long.BYTES;
485 final byte[] seed = new byte[size];
486 for (int start = 0; start < size; start++) {
487 final int from = start;
488 for (int end = start; end < size; end++) {
489 final int to = end;
490 Arrays.fill(seed, (byte) 0);
491 final int before = calls[0];
492 SeedFactory.ensureNonZero(seed, from, to, rng);
493
494
495 for (int i = 0; i < from; i++) {
496 final int index = i;
497 Assertions.assertEquals(0, seed[i],
498 () -> String.format("[%d, %d) zero at position %d should not be modified",
499 from, to, index));
500 }
501 if (to > from) {
502 byte allBits = 0;
503 for (int i = from; i < to; i++) {
504 allBits |= seed[i];
505 }
506 Assertions.assertNotEquals(0, allBits,
507 () -> String.format("[%d, %d) should not be all zero", from, to));
508
509 Assertions.assertNotEquals(before, calls[0],
510 () -> String.format("[%d, %d) should use the random source", from, to));
511 } else {
512
513 Assertions.assertEquals(before, calls[0],
514 () -> String.format("[%d, %d) should not use the random source", from, to));
515 }
516 for (int i = to; i < size; i++) {
517 final int index = i;
518 Assertions.assertEquals(0, seed[i],
519 () -> String.format("[%d, %d) zero at position %d should not be modified",
520 from, to, index));
521 }
522 }
523 }
524 }
525
526 @Test
527 void testEnsureNonZeroValue() {
528 final long expected = 345;
529 RandomLongSource source = new RandomLongSource() {
530 @Override
531 public long next() {
532 return expected;
533 }
534 };
535 Assertions.assertEquals(expected, SeedFactory.ensureNonZero(source, 0),
536 "Zero should be replaced using the random source");
537 for (final long nonZero : new long[] {Long.MIN_VALUE, -1, 1, 9876654321L, Long.MAX_VALUE}) {
538 Assertions.assertEquals(nonZero, SeedFactory.ensureNonZero(source, nonZero),
539 "Non-zero should be unmodified");
540 }
541 }
542 }