1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.hipparchus.random;
23
24 import org.hipparchus.UnitTestUtils;
25 import org.hipparchus.exception.MathIllegalArgumentException;
26 import org.hipparchus.util.FastMath;
27 import org.junit.jupiter.api.BeforeEach;
28 import org.junit.jupiter.api.Test;
29
30 import java.util.Arrays;
31
32 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
33 import static org.junit.jupiter.api.Assertions.assertThrows;
34 import static org.junit.jupiter.api.Assertions.assertTrue;
35 import static org.junit.jupiter.api.Assertions.fail;
36
37
38
39
40
41
42
43
44
45
46
47 public abstract class RandomGeneratorAbstractTest extends RandomDataGeneratorTest {
48
49
50 protected RandomGenerator generator;
51
52
53
54
55
56 protected abstract RandomGenerator makeGenerator();
57
58
59
60
61 public RandomGeneratorAbstractTest() {
62 generator = makeGenerator();
63 randomData = RandomDataGenerator.of(generator);
64 }
65
66
67
68
69 @BeforeEach
70 public void setUp() {
71 generator = makeGenerator();
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86 @Test
87 public void testNextIntDirect() {
88
89 int[] testValues = new int[] {4, 10, 12, 32, 100, 10000, 0, 0, 0, 0};
90 for (int i = 6; i < 10; i++) {
91 final int val = generator.nextInt();
92 testValues[i] = val < 0 ? -val : val + 1;
93 }
94
95 final int numTests = 1000;
96 for (int i = 0; i < testValues.length; i++) {
97 final int n = testValues[i];
98
99 int[] binUpperBounds;
100 if (n < 32) {
101 binUpperBounds = new int[n];
102 for (int k = 0; k < n; k++) {
103 binUpperBounds[k] = k;
104 }
105 } else {
106 binUpperBounds = new int[10];
107 final int step = n / 10;
108 for (int k = 0; k < 9; k++) {
109 binUpperBounds[k] = (k + 1) * step;
110 }
111 binUpperBounds[9] = n - 1;
112 }
113
114 int numFailures = 0;
115 final int binCount = binUpperBounds.length;
116 final long[] observed = new long[binCount];
117 final double[] expected = new double[binCount];
118 expected[0] = binUpperBounds[0] == 0 ? (double) smallSampleSize / (double) n :
119 (double) ((binUpperBounds[0] + 1) * smallSampleSize) / (double) n;
120 for (int k = 1; k < binCount; k++) {
121 expected[k] = (double) smallSampleSize *
122 (double) (binUpperBounds[k] - binUpperBounds[k - 1]) / n;
123 }
124 for (int j = 0; j < numTests; j++) {
125 Arrays.fill(observed, 0);
126 for (int k = 0; k < smallSampleSize; k++) {
127 final int value = generator.nextInt(n);
128 assertTrue((value >= 0) && (value < n),"nextInt range");
129 for (int l = 0; l < binCount; l++) {
130 if (binUpperBounds[l] >= value) {
131 observed[l]++;
132 break;
133 }
134 }
135 }
136 if (UnitTestUtils.chiSquareTest(expected, observed) < 0.01) {
137 numFailures++;
138 }
139 }
140 if ((double) numFailures / (double) numTests > 0.02) {
141 fail("Too many failures for n = " + n +
142 " " + numFailures + " out of " + numTests + " tests failed.");
143 }
144 }
145 }
146
147 @Test
148 public void testNextLongDirect() {
149 long q1 = Long.MAX_VALUE/4;
150 long q2 = 2 * q1;
151 long q3 = 3 * q1;
152
153 long[] observed = new long[4];
154 long val = 0;
155 for (int i = 0; i < smallSampleSize; i++) {
156 val = generator.nextLong();
157 val = val < 0 ? -val : val;
158 if (val < q1) {
159 observed[0] = ++observed[0];
160 } else if (val < q2) {
161 observed[1] = ++observed[1];
162 } else if (val < q3) {
163 observed[2] = ++observed[2];
164 } else {
165 observed[3] = ++observed[3];
166 }
167 }
168
169
170
171
172 assertTrue(UnitTestUtils.chiSquare(expected,observed) < 16.27,
173 "chi-square test -- will fail about 1 in 1000 times");
174 }
175
176 @Test
177 public void testNextBooleanDirect() {
178 long halfSampleSize = smallSampleSize / 2;
179 double[] expected = {halfSampleSize, halfSampleSize};
180 long[] observed = new long[2];
181 for (int i=0; i<smallSampleSize; i++) {
182 if (generator.nextBoolean()) {
183 observed[0]++;
184 } else {
185 observed[1]++;
186 }
187 }
188
189
190
191 assertTrue(UnitTestUtils.chiSquare(expected,observed) < 10.828,
192 "chi-square test -- will fail about 1 in 1000 times");
193 }
194
195 @Test
196 public void testNextFloatDirect() {
197 long[] observed = new long[4];
198 for (int i=0; i<smallSampleSize; i++) {
199 float val = generator.nextFloat();
200 if (val < 0.25) {
201 observed[0] = ++observed[0];
202 } else if (val < 0.5) {
203 observed[1] = ++observed[1];
204 } else if (val < 0.75) {
205 observed[2] = ++observed[2];
206 } else {
207 observed[3] = ++observed[3];
208 }
209 }
210
211
212
213
214 assertTrue(UnitTestUtils.chiSquare(expected,observed) < 16.27,
215 "chi-square test -- will fail about 1 in 1000 times");
216 }
217
218 @Test
219 public void testNextDouble() {
220 final double[] sample = new double[10000];
221 final double[] expected = new double[100];
222 final long[] observed = new long[100];
223 Arrays.fill(expected, 100d);
224 for (int i = 0; i < sample.length; i++) {
225 sample[i] = generator.nextDouble();
226 int j = 0;
227 while (sample[i] < j / 100) {
228 j++;
229 }
230 observed[j] = observed[j]++;
231 }
232 UnitTestUtils.customAssertChiSquareAccept(expected, observed, 0.01);
233 }
234
235
236 @Test
237 public void testNextIntPrecondition1() {
238 assertThrows(MathIllegalArgumentException.class, () -> {
239 generator.nextInt(-1);
240 });
241 }
242
243 @Test
244 public void testNextIntPrecondition2() {
245 assertThrows(MathIllegalArgumentException.class, () -> {
246 generator.nextInt(0);
247 });
248 }
249
250 @Test
251 public void testNextInt2() {
252 int walk = 0;
253 final int N = 10000;
254 for (int k = 0; k < N; ++k) {
255 if (generator.nextInt() >= 0) {
256 ++walk;
257 } else {
258 --walk;
259 }
260 }
261 assertTrue(FastMath.abs(walk) < FastMath.sqrt(N) * 2.576,
262 "Walked too far astray: " + walk + "\nNote: This " +
263 "test will fail randomly about 1 in 100 times.");
264 }
265
266 @Test
267 public void testNextLong2() {
268 int walk = 0;
269 final int N = 1000;
270 for (int k = 0; k < N; ++k) {
271 if (generator.nextLong() >= 0) {
272 ++walk;
273 } else {
274 --walk;
275 }
276 }
277 assertTrue(FastMath.abs(walk) < FastMath.sqrt(N) * 2.576,
278 "Walked too far astray: " + walk + "\nNote: This " +
279 "test will fail randomly about 1 in 100 times.");
280 }
281
282 @Test
283 public void testNextBoolean2() {
284 int walk = 0;
285 final int N = 10000;
286 for (int k = 0; k < N; ++k) {
287 if (generator.nextBoolean()) {
288 ++walk;
289 } else {
290 --walk;
291 }
292 }
293 assertTrue(FastMath.abs(walk) < FastMath.sqrt(N) * 2.576,
294 "Walked too far astray: " + walk + "\nNote: This " +
295 "test will fail randomly about 1 in 100 times.");
296 }
297
298 @Test
299 public void testNextBytes() {
300 long[] count = new long[256];
301 byte[] bytes = new byte[10];
302 double[] expected = new double[256];
303 final int sampleSize = 100000;
304
305 for (int i = 0; i < 256; i++) {
306 expected[i] = (double) sampleSize / 265f;
307 }
308
309 for (int k = 0; k < sampleSize; ++k) {
310 generator.nextBytes(bytes);
311 for (byte b : bytes) {
312 ++count[b + 128];
313 }
314 }
315
316 UnitTestUtils.customAssertChiSquareAccept(expected, count, 0.001);
317 }
318
319
320 @Test
321 public void testNextBytesChunks() {
322 final int[] chunkSizes = { 4, 8, 12, 16 };
323 final int[] chunks = { 1, 2, 3, 4, 5 };
324 for (int chunkSize : chunkSizes) {
325 for (int numChunks : chunks) {
326 checkNextBytesChunks(chunkSize, numChunks);
327 }
328 }
329 }
330
331 @Test
332 public void testSeeding() {
333
334 RandomGenerator gen = makeGenerator();
335 RandomGenerator gen1 = makeGenerator();
336 checkSameSequence(gen, gen1);
337
338
339 gen.setSeed(100);
340 gen1 = makeGenerator();
341 gen1.setSeed(100);
342 checkSameSequence(gen, gen1);
343 }
344
345 private void checkSameSequence(RandomGenerator gen1, RandomGenerator gen2) {
346 final int len = 11;
347 final double[][] values = new double[2][len];
348 for (int i = 0; i < len; i++) {
349 values[0][i] = gen1.nextDouble();
350 }
351 for (int i = 0; i < len; i++) {
352 values[1][i] = gen2.nextDouble();
353 }
354 assertTrue(Arrays.equals(values[0], values[1]));
355 for (int i = 0; i < len; i++) {
356 values[0][i] = gen1.nextFloat();
357 }
358 for (int i = 0; i < len; i++) {
359 values[1][i] = gen2.nextFloat();
360 }
361 assertTrue(Arrays.equals(values[0], values[1]));
362 for (int i = 0; i < len; i++) {
363 values[0][i] = gen1.nextInt();
364 }
365 for (int i = 0; i < len; i++) {
366 values[1][i] = gen2.nextInt();
367 }
368 assertTrue(Arrays.equals(values[0], values[1]));
369 for (int i = 0; i < len; i++) {
370 values[0][i] = gen1.nextLong();
371 }
372 for (int i = 0; i < len; i++) {
373 values[1][i] = gen2.nextLong();
374 }
375 assertTrue(Arrays.equals(values[0], values[1]));
376 for (int i = 0; i < len; i++) {
377 values[0][i] = gen1.nextInt(len);
378 }
379 for (int i = 0; i < len; i++) {
380 values[1][i] = gen2.nextInt(len);
381 }
382 assertTrue(Arrays.equals(values[0], values[1]));
383 for (int i = 0; i < len; i++) {
384 values[0][i] = gen1.nextBoolean() ? 1 : 0;
385 }
386 for (int i = 0; i < len; i++) {
387 values[1][i] = gen2.nextBoolean() ? 1 : 0;
388 }
389 assertTrue(Arrays.equals(values[0], values[1]));
390 for (int i = 0; i < len; i++) {
391 values[0][i] = gen1.nextGaussian();
392 }
393 for (int i = 0; i < len; i++) {
394 values[1][i] = gen2.nextGaussian();
395 }
396 assertTrue(Arrays.equals(values[0], values[1]));
397 }
398
399
400 private void checkNextBytesChunks(int chunkSize,
401 int numChunks) {
402 final RandomGenerator rg = makeGenerator();
403 final long seed = 1234567L;
404
405 final byte[] b1 = new byte[chunkSize * numChunks];
406 final byte[] b2 = new byte[chunkSize];
407
408
409 rg.setSeed(seed);
410 rg.nextBytes(b1);
411
412
413 rg.setSeed(seed);
414
415 for (int i = 0; i < numChunks; i++) {
416 rg.nextBytes(b2);
417 }
418
419
420 final byte[] b3 = new byte[chunkSize];
421 System.arraycopy(b1, b1.length - b3.length, b3, 0, b3.length);
422
423
424 assertArrayEquals(b2, b3, "chunkSize=" + chunkSize + " numChunks=" + numChunks);
425 }
426
427 @Override
428 @Test
429 public void testNextZipf() {
430
431 }
432 }