View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.random;
23  
24  import org.hipparchus.UnitTestUtils;
25  import org.hipparchus.distribution.continuous.BetaDistribution;
26  import org.hipparchus.distribution.continuous.EnumeratedRealDistribution;
27  import org.hipparchus.distribution.continuous.ExponentialDistribution;
28  import org.hipparchus.distribution.continuous.GammaDistribution;
29  import org.hipparchus.distribution.continuous.NormalDistribution;
30  import org.hipparchus.distribution.discrete.EnumeratedIntegerDistribution;
31  import org.hipparchus.distribution.discrete.PoissonDistribution;
32  import org.hipparchus.distribution.discrete.ZipfDistribution;
33  import org.hipparchus.exception.MathIllegalArgumentException;
34  import org.hipparchus.util.FastMath;
35  import org.junit.jupiter.api.Test;
36  
37  import java.text.DecimalFormat;
38  import java.util.ArrayList;
39  import java.util.HashSet;
40  import java.util.List;
41  
42  import static org.junit.jupiter.api.Assertions.assertEquals;
43  import static org.junit.jupiter.api.Assertions.assertFalse;
44  import static org.junit.jupiter.api.Assertions.assertThrows;
45  import static org.junit.jupiter.api.Assertions.assertTrue;
46  import static org.junit.jupiter.api.Assertions.fail;
47  
48  /**
49   * Test cases for the RandomDataGenerator class.
50   *
51   */
52  public class RandomDataGeneratorTest {
53  
54      public RandomDataGeneratorTest() {
55          randomData = RandomDataGenerator.of(new Well19937c());
56          randomData.setSeed(100);
57      }
58  
59      protected final long smallSampleSize = 1000;
60      protected final double[] expected = { 250, 250, 250, 250 };
61      protected final int largeSampleSize = 10000;
62      private final String[] hex = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
63              "a", "b", "c", "d", "e", "f" };
64      protected RandomDataGenerator randomData = null;
65  
66      @Test
67      void testNextIntExtremeValues() {
68          int x = randomData.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
69          int y = randomData.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
70          assertFalse(x == y);
71      }
72  
73      @Test
74      void testNextLongExtremeValues() {
75          long x = randomData.nextLong(Long.MIN_VALUE, Long.MAX_VALUE);
76          long y = randomData.nextLong(Long.MIN_VALUE, Long.MAX_VALUE);
77          assertFalse(x == y);
78      }
79  
80      @Test
81      void testNextUniformExtremeValues() {
82          double x = randomData.nextUniform(-Double.MAX_VALUE, Double.MAX_VALUE);
83          double y = randomData.nextUniform(-Double.MAX_VALUE, Double.MAX_VALUE);
84          assertFalse(x == y);
85          assertFalse(Double.isNaN(x));
86          assertFalse(Double.isNaN(y));
87          assertFalse(Double.isInfinite(x));
88          assertFalse(Double.isInfinite(y));
89      }
90  
91      @Test
92      void testNextIntIAE() {
93          try {
94              randomData.nextInt(4, 3);
95              fail("MathIllegalArgumentException expected");
96          } catch (MathIllegalArgumentException ex) {
97              // ignored
98          }
99      }
100 
101     @Test
102     void testNextIntNegativeToPositiveRange() {
103         for (int i = 0; i < 5; i++) {
104             checkNextIntUniform(-3, 5);
105             checkNextIntUniform(-3, 6);
106         }
107     }
108 
109     @Test
110     void testNextIntNegativeRange() {
111         for (int i = 0; i < 5; i++) {
112             checkNextIntUniform(-7, -4);
113             checkNextIntUniform(-15, -2);
114             checkNextIntUniform(Integer.MIN_VALUE + 1, Integer.MIN_VALUE + 12);
115         }
116     }
117 
118     @Test
119     void testNextIntPositiveRange() {
120         for (int i = 0; i < 5; i++) {
121             checkNextIntUniform(0, 3);
122             checkNextIntUniform(2, 12);
123             checkNextIntUniform(1,2);
124             checkNextIntUniform(Integer.MAX_VALUE - 12, Integer.MAX_VALUE - 1);
125         }
126     }
127 
128     private void checkNextIntUniform(int min, int max) {
129         final int len = max - min + 1;
130         final UnitTestUtils.Frequency<Integer> freq = new UnitTestUtils.Frequency<Integer>();
131         for (int i = 0; i < smallSampleSize; i++) {
132             final int value = randomData.nextInt(min, max);
133             assertTrue((value >= min) && (value <= max), "nextInt range");
134             freq.addValue(value);
135         }
136         final long[] observed = new long[len];
137         for (int i = 0; i < len; i++) {
138             observed[i] = freq.getCount(min + i);
139         }
140         final double[] expected = new double[len];
141         for (int i = 0; i < len; i++) {
142             expected[i] = 1d / len;
143         }
144 
145         UnitTestUtils.customAssertChiSquareAccept(expected, observed, 0.001);
146     }
147 
148     @Test
149     void testNextIntWideRange() {
150         int lower = -0x6543210F;
151         int upper =  0x456789AB;
152         int max   = Integer.MIN_VALUE;
153         int min   = Integer.MAX_VALUE;
154         for (int i = 0; i < 1000000; ++i) {
155             int r = randomData.nextInt(lower, upper);
156             max = FastMath.max(max, r);
157             min = FastMath.min(min, r);
158             assertTrue(r >= lower);
159             assertTrue(r <= upper);
160         }
161         double ratio = (((double) max)   - ((double) min)) /
162                        (((double) upper) - ((double) lower));
163         assertTrue(ratio > 0.99999);
164     }
165 
166     @Test
167     void testNextLongIAE() {
168         try {
169             randomData.nextLong(4, 3);
170             fail("MathIllegalArgumentException expected");
171         } catch (MathIllegalArgumentException ex) {
172             // ignored
173         }
174     }
175 
176     @Test
177     void testNextLongNegativeToPositiveRange() {
178         for (int i = 0; i < 5; i++) {
179             checkNextLongUniform(-3, 5);
180             checkNextLongUniform(-3, 6);
181         }
182     }
183 
184     @Test
185     void testNextLongNegativeRange() {
186         for (int i = 0; i < 5; i++) {
187             checkNextLongUniform(-7, -4);
188             checkNextLongUniform(-15, -2);
189             checkNextLongUniform(Long.MIN_VALUE + 1, Long.MIN_VALUE + 12);
190         }
191     }
192 
193     @Test
194     void testNextLongPositiveRange() {
195         for (int i = 0; i < 5; i++) {
196             checkNextLongUniform(0, 3);
197             checkNextLongUniform(2, 12);
198             checkNextLongUniform(Long.MAX_VALUE - 12, Long.MAX_VALUE - 1);
199         }
200     }
201 
202     private void checkNextLongUniform(long min, long max) {
203         final int len = ((int) (max - min)) + 1;
204         final UnitTestUtils.Frequency<Integer> freq = new UnitTestUtils.Frequency<Integer>();
205         for (int i = 0; i < smallSampleSize; i++) {
206             final long value = randomData.nextLong(min, max);
207             assertTrue((value >= min) && (value <= max),
208                               "nextLong range: " + value + " " + min + " " + max);
209             freq.addValue((int)value);
210         }
211         final long[] observed = new long[len];
212         for (int i = 0; i < len; i++) {
213             observed[i] = freq.getCount((int) min + i);
214         }
215         final double[] expected = new double[len];
216         for (int i = 0; i < len; i++) {
217             expected[i] = 1d / len;
218         }
219 
220         UnitTestUtils.customAssertChiSquareAccept(expected, observed, 0.01);
221     }
222 
223     @Test
224     void testNextLongWideRange() {
225         long lower = -0x6543210FEDCBA987L;
226         long upper =  0x456789ABCDEF0123L;
227         long max = Long.MIN_VALUE;
228         long min = Long.MAX_VALUE;
229         for (int i = 0; i < 10000000; ++i) {
230             long r = randomData.nextLong(lower, upper);
231             max = FastMath.max(max, r);
232             min = FastMath.min(min, r);
233             assertTrue(r >= lower);
234             assertTrue(r <= upper);
235         }
236         double ratio = (((double) max)   - ((double) min)) /
237                        (((double) upper) - ((double) lower));
238         assertTrue(ratio > 0.99999);
239     }
240 
241     /**
242      * Make sure that empirical distribution of random Poisson(4)'s has P(X &lt;= 5)
243      * close to actual cumulative Poisson probability and that nextPoisson
244      * fails when mean is non-positive.
245      */
246     @Test
247     void testNextPoisson() {
248         try {
249             randomData.nextPoisson(0);
250             fail("zero mean -- expecting MathIllegalArgumentException");
251         } catch (MathIllegalArgumentException ex) {
252             // ignored
253         }
254         try {
255             randomData.nextPoisson(-1);
256             fail("negative mean supplied -- MathIllegalArgumentException expected");
257         } catch (MathIllegalArgumentException ex) {
258             // ignored
259         }
260         try {
261             randomData.nextPoisson(0);
262             fail("0 mean supplied -- MathIllegalArgumentException expected");
263         } catch (MathIllegalArgumentException ex) {
264             // ignored
265         }
266 
267         final double mean = 4.0d;
268         final int len = 5;
269         PoissonDistribution poissonDistribution = new PoissonDistribution(mean);
270         final UnitTestUtils.Frequency<Integer> freq = new UnitTestUtils.Frequency<Integer>();
271         randomData.setSeed(1000);
272         for (int i = 0; i < largeSampleSize; i++) {
273             freq.addValue(randomData.nextPoisson(mean));
274         }
275         final long[] observed = new long[len];
276         for (int i = 0; i < len; i++) {
277             observed[i] = freq.getCount(i + 1);
278         }
279         final double[] expected = new double[len];
280         for (int i = 0; i < len; i++) {
281             expected[i] = poissonDistribution.probability(i + 1) * largeSampleSize;
282         }
283 
284         UnitTestUtils.customAssertChiSquareAccept(expected, observed, 0.0001);
285     }
286 
287     @Test
288     void testNextPoissonConsistency() {
289 
290         // Small integral means
291         for (int i = 1; i < 100; i++) {
292             checkNextPoissonConsistency(i);
293         }
294         // non-integer means
295         for (int i = 1; i < 10; i++) {
296             checkNextPoissonConsistency(randomData.nextUniform(1, 1000));
297         }
298         // large means
299         for (int i = 1; i < 10; i++) {
300             checkNextPoissonConsistency(randomData.nextUniform(1000, 10000));
301         }
302     }
303 
304     /**
305      * Verifies that nextPoisson(mean) generates an empirical distribution of values
306      * consistent with PoissonDistributionImpl by generating 1000 values, computing a
307      * grouped frequency distribution of the observed values and comparing this distribution
308      * to the corresponding expected distribution computed using PoissonDistributionImpl.
309      * Uses ChiSquare test of goodness of fit to evaluate the null hypothesis that the
310      * distributions are the same. If the null hypothesis can be rejected with confidence
311      * 1 - alpha, the check fails.
312      */
313     public void checkNextPoissonConsistency(double mean) {
314         // Generate sample values
315         final int sampleSize = 1000;        // Number of deviates to generate
316         final int minExpectedCount = 7;     // Minimum size of expected bin count
317         long maxObservedValue = 0;
318         final double alpha = 0.001;         // Probability of false failure
319         UnitTestUtils.Frequency<Long> frequency = new UnitTestUtils.Frequency<Long>();
320         for (int i = 0; i < sampleSize; i++) {
321             long value = randomData.nextPoisson(mean);
322             if (value > maxObservedValue) {
323                 maxObservedValue = value;
324             }
325             frequency.addValue(value);
326         }
327 
328         /*
329          *  Set up bins for chi-square test.
330          *  Ensure expected counts are all at least minExpectedCount.
331          *  Start with upper and lower tail bins.
332          *  Lower bin = [0, lower); Upper bin = [upper, +inf).
333          */
334         PoissonDistribution poissonDistribution = new PoissonDistribution(mean);
335         int lower = 1;
336         while (poissonDistribution.cumulativeProbability(lower - 1) * sampleSize < minExpectedCount) {
337             lower++;
338         }
339         int upper = (int) (5 * mean);  // Even for mean = 1, not much mass beyond 5
340         while ((1 - poissonDistribution.cumulativeProbability(upper - 1)) * sampleSize < minExpectedCount) {
341             upper--;
342         }
343 
344         // Set bin width for interior bins.  For poisson, only need to look at end bins.
345         int binWidth = 0;
346         boolean widthSufficient = false;
347         double lowerBinMass = 0;
348         double upperBinMass = 0;
349         while (!widthSufficient) {
350             binWidth++;
351             lowerBinMass = poissonDistribution.probability(lower - 1, lower + binWidth - 1);
352             upperBinMass = poissonDistribution.probability(upper - binWidth - 1, upper - 1);
353             widthSufficient = FastMath.min(lowerBinMass, upperBinMass) * sampleSize >= minExpectedCount;
354         }
355 
356         /*
357          *  Determine interior bin bounds.  Bins are
358          *  [1, lower = binBounds[0]), [lower, binBounds[1]), [binBounds[1], binBounds[2]), ... ,
359          *    [binBounds[binCount - 2], upper = binBounds[binCount - 1]), [upper, +inf)
360          *
361          */
362         List<Integer> binBounds = new ArrayList<Integer>();
363         binBounds.add(lower);
364         int bound = lower + binWidth;
365         while (bound < upper - binWidth) {
366             binBounds.add(bound);
367             bound += binWidth;
368         }
369         binBounds.add(upper); // The size of bin [binBounds[binCount - 2], upper) satisfies binWidth <= size < 2*binWidth.
370 
371         // Compute observed and expected bin counts
372         final int binCount = binBounds.size() + 1;
373         long[] observed = new long[binCount];
374         double[] expected = new double[binCount];
375 
376         // Bottom bin
377         observed[0] = 0;
378         for (int i = 0; i < lower; i++) {
379             observed[0] += frequency.getCount((long)i);
380         }
381         expected[0] = poissonDistribution.cumulativeProbability(lower - 1) * sampleSize;
382 
383         // Top bin
384         observed[binCount - 1] = 0;
385         for (int i = upper; i <= maxObservedValue; i++) {
386             observed[binCount - 1] += frequency.getCount((long)i);
387         }
388         expected[binCount - 1] = (1 - poissonDistribution.cumulativeProbability(upper - 1)) * sampleSize;
389 
390         // Interior bins
391         for (int i = 1; i < binCount - 1; i++) {
392             observed[i] = 0;
393             for (int j = binBounds.get(i - 1); j < binBounds.get(i); j++) {
394                 observed[i] += frequency.getCount((long)j);
395             } // Expected count is (mass in [binBounds[i-1], binBounds[i])) * sampleSize
396             expected[i] = (poissonDistribution.cumulativeProbability(binBounds.get(i) - 1) -
397                 poissonDistribution.cumulativeProbability(binBounds.get(i - 1) -1)) * sampleSize;
398         }
399 
400         // Use chisquare test to verify that generated values are poisson(mean)-distributed
401         // Fail if we can reject null hypothesis that distributions are the same
402         if (UnitTestUtils.chiSquareTest(expected, observed) <  alpha) {
403             StringBuilder msgBuffer = new StringBuilder();
404             DecimalFormat df = new DecimalFormat("#.##");
405             msgBuffer.append("Chisquare test failed for mean = ");
406             msgBuffer.append(mean);
407             msgBuffer.append(" p-value = ");
408             msgBuffer.append(UnitTestUtils.chiSquareTest(expected, observed));
409             msgBuffer.append(" chisquare statistic = ");
410             msgBuffer.append(UnitTestUtils.chiSquare(expected, observed));
411             msgBuffer.append(". \n");
412             msgBuffer.append("bin\t\texpected\tobserved\n");
413             for (int i = 0; i < expected.length; i++) {
414                 msgBuffer.append("[");
415                 msgBuffer.append(i == 0 ? 1: binBounds.get(i - 1));
416                 msgBuffer.append(",");
417                 msgBuffer.append(i == binBounds.size() ? "inf": binBounds.get(i));
418                 msgBuffer.append(")");
419                 msgBuffer.append("\t\t");
420                 msgBuffer.append(df.format(expected[i]));
421                 msgBuffer.append("\t\t");
422                 msgBuffer.append(observed[i]);
423                 msgBuffer.append("\n");
424             }
425             msgBuffer.append("This test can fail randomly due to sampling error with probability ");
426             msgBuffer.append(alpha);
427             msgBuffer.append(".");
428             fail(msgBuffer.toString());
429         }
430     }
431 
432     /** test dispersion and failure modes for nextHex() */
433     @Test
434     void testNextHex() {
435         try {
436             randomData.nextHexString(-1);
437             fail("negative length supplied -- MathIllegalArgumentException expected");
438         } catch (MathIllegalArgumentException ex) {
439             // ignored
440         }
441         try {
442             randomData.nextHexString(0);
443             fail("zero length supplied -- MathIllegalArgumentException expected");
444         } catch (MathIllegalArgumentException ex) {
445             // ignored
446         }
447         String hexString = randomData.nextHexString(3);
448         if (hexString.length() != 3) {
449             fail("incorrect length for generated string");
450         }
451         hexString = randomData.nextHexString(1);
452         if (hexString.length() != 1) {
453             fail("incorrect length for generated string");
454         }
455         try {
456             hexString = randomData.nextHexString(0);
457             fail("zero length requested -- expecting MathIllegalArgumentException");
458         } catch (MathIllegalArgumentException ex) {
459             // ignored
460         }
461         UnitTestUtils.Frequency<String> f = new UnitTestUtils.Frequency<String> ();
462         for (int i = 0; i < smallSampleSize; i++) {
463             hexString = randomData.nextHexString(100);
464             if (hexString.length() != 100) {
465                 fail("incorrect length for generated string");
466             }
467             for (int j = 0; j < hexString.length(); j++) {
468                 f.addValue(hexString.substring(j, j + 1));
469             }
470         }
471         double[] expected = new double[16];
472         long[] observed = new long[16];
473         for (int i = 0; i < 16; i++) {
474             expected[i] = (double) smallSampleSize * 100 / 16;
475             observed[i] = f.getCount(hex[i]);
476         }
477         UnitTestUtils.customAssertChiSquareAccept(expected, observed, 0.001);
478     }
479 
480     @Test
481     void testNextUniformIAE() {
482         try {
483             randomData.nextUniform(4, 3);
484             fail("MathIllegalArgumentException expected");
485         } catch (MathIllegalArgumentException ex) {
486             // ignored
487         }
488         try {
489             randomData.nextUniform(0, Double.POSITIVE_INFINITY);
490             fail("MathIllegalArgumentException expected");
491         } catch (MathIllegalArgumentException ex) {
492             // ignored
493         }
494         try {
495             randomData.nextUniform(Double.NEGATIVE_INFINITY, 0);
496             fail("MathIllegalArgumentException expected");
497         } catch (MathIllegalArgumentException ex) {
498             // ignored
499         }
500         try {
501             randomData.nextUniform(0, Double.NaN);
502             fail("MathIllegalArgumentException expected");
503         } catch (MathIllegalArgumentException ex) {
504             // ignored
505         }
506         try {
507             randomData.nextUniform(Double.NaN, 0);
508             fail("MathIllegalArgumentException expected");
509         } catch (MathIllegalArgumentException ex) {
510             // ignored
511         }
512     }
513 
514     @Test
515     void testNextUniformUniformPositiveBounds() {
516         for (int i = 0; i < 5; i++) {
517             checkNextUniformUniform(0, 10);
518         }
519     }
520 
521     @Test
522     void testNextUniformUniformNegativeToPositiveBounds() {
523         for (int i = 0; i < 5; i++) {
524             checkNextUniformUniform(-3, 5);
525         }
526     }
527 
528     @Test
529     void testNextUniformUniformNegaiveBounds() {
530         for (int i = 0; i < 5; i++) {
531             checkNextUniformUniform(-7, -3);
532         }
533     }
534 
535     @Test
536     void testNextUniformUniformMaximalInterval() {
537         for (int i = 0; i < 5; i++) {
538             checkNextUniformUniform(-Double.MAX_VALUE, Double.MAX_VALUE);
539         }
540     }
541 
542     private void checkNextUniformUniform(double min, double max) {
543         // Set up bin bounds - min, binBound[0], ..., binBound[binCount-2], max
544         final int binCount = 5;
545         final double binSize = max / binCount - min/binCount; // Prevent overflow in extreme value case
546         final double[] binBounds = new double[binCount - 1];
547         binBounds[0] = min + binSize;
548         for (int i = 1; i < binCount - 1; i++) {
549             binBounds[i] = binBounds[i - 1] + binSize;  // + instead of * to avoid overflow in extreme case
550         }
551 
552         UnitTestUtils.Frequency<Integer> freq = new UnitTestUtils.Frequency<Integer>();
553         for (int i = 0; i < smallSampleSize; i++) {
554             final double value = randomData.nextUniform(min, max);
555             assertTrue((value > min) && (value < max), "nextUniform range");
556             // Find bin
557             int j = 0;
558             while (j < binCount - 1 && value > binBounds[j]) {
559                 j++;
560             }
561             freq.addValue(j);
562         }
563 
564         final long[] observed = new long[binCount];
565         for (int i = 0; i < binCount; i++) {
566             observed[i] = freq.getCount(i);
567         }
568         final double[] expected = new double[binCount];
569         for (int i = 0; i < binCount; i++) {
570             expected[i] = 1d / binCount;
571         }
572 
573         UnitTestUtils.customAssertChiSquareAccept(expected, observed, 0.01);
574     }
575 
576     /** test exclusive endpoints of nextUniform **/
577     @Test
578     void testNextUniformExclusiveEndpoints() {
579         for (int i = 0; i < 1000; i++) {
580             double u = randomData.nextUniform(0.99, 1);
581             assertTrue(u > 0.99 && u < 1);
582         }
583     }
584 
585     /** test failure modes and distribution of nextGaussian() */
586     @Test
587     void testNextGaussian() {
588         try {
589             randomData.nextNormal(0, 0);
590             fail("zero sigma -- MathIllegalArgumentException expected");
591         } catch (MathIllegalArgumentException ex) {
592             // ignored
593         }
594         double[] quartiles = UnitTestUtils.getDistributionQuartiles(new NormalDistribution(0,1));
595         long[] counts = new long[4];
596         randomData.setSeed(1000);
597         for (int i = 0; i < 1000; i++) {
598             double value = randomData.nextNormal(0, 1);
599             UnitTestUtils.updateCounts(value, counts, quartiles);
600         }
601         UnitTestUtils.customAssertChiSquareAccept(expected, counts, 0.001);
602     }
603 
604     /** test failure modes and distribution of nextExponential() */
605     @Test
606     void testNextExponential() {
607         try {
608             randomData.nextExponential(-1);
609             fail("negative mean -- expecting MathIllegalArgumentException");
610         } catch (MathIllegalArgumentException ex) {
611             // ignored
612         }
613         try {
614             randomData.nextExponential(0);
615             fail("zero mean -- expecting MathIllegalArgumentException");
616         } catch (MathIllegalArgumentException ex) {
617             // ignored
618         }
619         double[] quartiles;
620         long[] counts;
621 
622         // Mean 1
623         quartiles = UnitTestUtils.getDistributionQuartiles(new ExponentialDistribution(1));
624         counts = new long[4];
625         randomData.setSeed(1000);
626         for (int i = 0; i < 1000; i++) {
627             double value = randomData.nextExponential(1);
628             UnitTestUtils.updateCounts(value, counts, quartiles);
629         }
630         UnitTestUtils.customAssertChiSquareAccept(expected, counts, 0.001);
631 
632         // Mean 5
633         quartiles = UnitTestUtils.getDistributionQuartiles(new ExponentialDistribution(5));
634         counts = new long[4];
635         randomData.setSeed(1000);
636         for (int i = 0; i < 1000; i++) {
637             double value = randomData.nextExponential(5);
638             UnitTestUtils.updateCounts(value, counts, quartiles);
639         }
640         UnitTestUtils.customAssertChiSquareAccept(expected, counts, 0.001);
641     }
642 
643     /** test reseeding, algorithm/provider games */
644     @Test
645     void testConfig() {
646         randomData.setSeed(1000);
647         double v = randomData.nextUniform(0, 1);
648         randomData.setSeed(System.currentTimeMillis());
649         assertTrue(FastMath.abs(v - randomData.nextUniform(0, 1)) > 10E-12, "different seeds");
650         randomData.setSeed(1000);
651         assertEquals(v, randomData.nextUniform(0, 1), 10E-12, "same seeds");
652     }
653 
654     /** tests for nextSample() sampling from Collection */
655     @Test
656     void testNextSample() {
657         Object[][] c = { { "0", "1" }, { "0", "2" }, { "0", "3" },
658                 { "0", "4" }, { "1", "2" }, { "1", "3" }, { "1", "4" },
659                 { "2", "3" }, { "2", "4" }, { "3", "4" } };
660         long[] observed = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
661         double[] expected = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 };
662 
663         HashSet<Object> cPop = new HashSet<Object>(); // {0,1,2,3,4}
664         for (int i = 0; i < 5; i++) {
665             cPop.add(Integer.toString(i));
666         }
667 
668         Object[] sets = new Object[10]; // 2-sets from 5
669         for (int i = 0; i < 10; i++) {
670             HashSet<Object> hs = new HashSet<Object>();
671             hs.add(c[i][0]);
672             hs.add(c[i][1]);
673             sets[i] = hs;
674         }
675 
676         for (int i = 0; i < 1000; i++) {
677             Object[] cSamp = randomData.nextSample(cPop, 2);
678             observed[findSample(sets, cSamp)]++;
679         }
680 
681         /*
682          * Use ChiSquare dist with df = 10-1 = 9, alpha = .001 Change to 21.67
683          * for alpha = .01
684          */
685         assertTrue(UnitTestUtils.chiSquare(expected, observed) < 27.88,
686                 "chi-square test -- will fail about 1 in 1000 times");
687 
688         // Make sure sample of size = size of collection returns same collection
689         HashSet<Object> hs = new HashSet<Object>();
690         hs.add("one");
691         Object[] one = randomData.nextSample(hs, 1);
692         String oneString = (String) one[0];
693         if ((one.length != 1) || !oneString.equals("one")) {
694             fail("bad sample for set size = 1, sample size = 1");
695         }
696 
697         // Make sure we fail for sample size > collection size
698         try {
699             one = randomData.nextSample(hs, 2);
700             fail("sample size > set size, expecting MathIllegalArgumentException");
701         } catch (MathIllegalArgumentException ex) {
702             // ignored
703         }
704 
705         // Make sure we fail for empty collection
706         try {
707             hs = new HashSet<Object>();
708             one = randomData.nextSample(hs, 0);
709             fail("n = k = 0, expecting MathIllegalArgumentException");
710         } catch (MathIllegalArgumentException ex) {
711             // ignored
712         }
713     }
714 
715     @SuppressWarnings("unchecked")
716     private int findSample(Object[] u, Object[] samp) {
717         for (int i = 0; i < u.length; i++) {
718             HashSet<Object> set = (HashSet<Object>) u[i];
719             HashSet<Object> sampSet = new HashSet<Object>();
720             for (int j = 0; j < samp.length; j++) {
721                 sampSet.add(samp[j]);
722             }
723             if (set.equals(sampSet)) {
724                 return i;
725             }
726         }
727         fail("sample not found:{" + samp[0] + "," + samp[1] + "}");
728         return -1;
729     }
730 
731     /** tests for nextPermutation */
732     @Test
733     void testNextPermutation() {
734         int[][] p = { { 0, 1, 2 }, { 0, 2, 1 }, { 1, 0, 2 }, { 1, 2, 0 },
735                 { 2, 0, 1 }, { 2, 1, 0 } };
736         long[] observed = { 0, 0, 0, 0, 0, 0 };
737         double[] expected = { 100, 100, 100, 100, 100, 100 };
738 
739         for (int i = 0; i < 600; i++) {
740             int[] perm = randomData.nextPermutation(3, 3);
741             observed[findPerm(p, perm)]++;
742         }
743 
744         String[] labels = {"{0, 1, 2}", "{ 0, 2, 1 }", "{ 1, 0, 2 }",
745                 "{ 1, 2, 0 }", "{ 2, 0, 1 }", "{ 2, 1, 0 }"};
746         UnitTestUtils.customAssertChiSquareAccept(labels, expected, observed, 0.001);
747 
748         // Check size = 1 boundary case
749         int[] perm = randomData.nextPermutation(1, 1);
750         if ((perm.length != 1) || (perm[0] != 0)) {
751             fail("bad permutation for n = 1, sample k = 1");
752 
753             // Make sure we fail for k size > n
754             try {
755                 perm = randomData.nextPermutation(2, 3);
756                 fail("permutation k > n, expecting MathIllegalArgumentException");
757             } catch (MathIllegalArgumentException ex) {
758                 // ignored
759             }
760 
761             // Make sure we fail for n = 0
762             try {
763                 perm = randomData.nextPermutation(0, 0);
764                 fail("permutation k = n = 0, expecting MathIllegalArgumentException");
765             } catch (MathIllegalArgumentException ex) {
766                 // ignored
767             }
768 
769             // Make sure we fail for k < n < 0
770             try {
771                 perm = randomData.nextPermutation(-1, -3);
772                 fail("permutation k < n < 0, expecting MathIllegalArgumentException");
773             } catch (MathIllegalArgumentException ex) {
774                 // ignored
775             }
776 
777         }
778     }
779 
780     private int findPerm(int[][] p, int[] samp) {
781         for (int i = 0; i < p.length; i++) {
782             boolean good = true;
783             for (int j = 0; j < samp.length; j++) {
784                 if (samp[j] != p[i][j]) {
785                     good = false;
786                 }
787             }
788             if (good) {
789                 return i;
790             }
791         }
792         fail("permutation not found");
793         return -1;
794     }
795 
796     @Test
797     void testNextBeta() {
798         double[] quartiles = UnitTestUtils.getDistributionQuartiles(new BetaDistribution(2,5));
799         long[] counts = new long[4];
800         randomData.setSeed(1000);
801         for (int i = 0; i < 1000; i++) {
802             double value = randomData.nextBeta(2, 5);
803             UnitTestUtils.updateCounts(value, counts, quartiles);
804         }
805         UnitTestUtils.customAssertChiSquareAccept(expected, counts, 0.001);
806     }
807 
808     @Test
809     void testNextGamma() {
810         double[] quartiles;
811         long[] counts;
812 
813         // Tests shape > 1, one case in the rejection sampling
814         quartiles = UnitTestUtils.getDistributionQuartiles(new GammaDistribution(4, 2));
815         counts = new long[4];
816         randomData.setSeed(1000);
817         for (int i = 0; i < 1000; i++) {
818             double value = randomData.nextGamma(4, 2);
819             UnitTestUtils.updateCounts(value, counts, quartiles);
820         }
821         UnitTestUtils.customAssertChiSquareAccept(expected, counts, 0.001);
822 
823         // Tests shape <= 1, another case in the rejection sampling
824         quartiles = UnitTestUtils.getDistributionQuartiles(new GammaDistribution(0.3, 3));
825         counts = new long[4];
826         randomData.setSeed(1000);
827         for (int i = 0; i < 1000; i++) {
828             double value = randomData.nextGamma(0.3, 3);
829             UnitTestUtils.updateCounts(value, counts, quartiles);
830         }
831         UnitTestUtils.customAssertChiSquareAccept(expected, counts, 0.001);
832     }
833 
834     @Test
835     void testNextGamma2() {
836         final RandomDataGenerator randomDataGenerator = new RandomDataGenerator(1000);
837         final int sampleSize = 1000;
838         final double alpha = 0.001;
839         GammaDistribution dist = new GammaDistribution(3, 1);
840         double[] values = randomDataGenerator.nextDeviates(dist, sampleSize);
841         UnitTestUtils.customAssertGTest(dist, values, alpha);
842         dist = new GammaDistribution(.4, 2);
843         values = randomDataGenerator.nextDeviates(dist, sampleSize);
844         UnitTestUtils.customAssertGTest(dist, values, alpha);
845     }
846 
847     @Test
848     void testNextBeta2() {
849         final double[] alphaBetas = {0.1, 1, 10, 100, 1000};
850         final RandomGenerator random = new Well1024a(0x7829862c82fec2dal);
851         final RandomDataGenerator randomDataGenerator = RandomDataGenerator.of(random);
852         final int sampleSize = 1000;
853         final double alphaCrit = 0.001;
854         for (final double alpha : alphaBetas) {
855             for (final double beta : alphaBetas) {
856                 final BetaDistribution betaDistribution = new BetaDistribution(alpha, beta);
857                 final double[] values = randomDataGenerator.nextDeviates(betaDistribution, sampleSize);
858                 UnitTestUtils.customAssertGTest(betaDistribution, values, alphaCrit);
859             }
860         }
861     }
862 
863     @Test
864     void testNextZipf() {
865         int sampleSize = 1000;
866 
867         int[] numPointsValues = {
868             2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100
869         };
870         double[] exponentValues = {
871             1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 2e-1, 5e-1,
872             1. - 1e-9, 1.0, 1. + 1e-9, 1.1, 1.2, 1.3, 1.5, 1.6, 1.7, 1.8, 2.0,
873             2.5, 3.0, 4., 5., 6., 7., 8., 9., 10., 20., 30., 100., 150.
874         };
875 
876         for (int numPoints : numPointsValues) {
877             for (double exponent : exponentValues) {
878                 double weightSum = 0.;
879                 double[] weights = new double[numPoints];
880                 for (int i = numPoints; i>=1; i-=1) {
881                     weights[i-1] = Math.pow(i, -exponent);
882                     weightSum += weights[i-1];
883                 }
884                 // use fixed seed, the test is expected to fail for more than 50% of all seeds because each test case can fail
885                 // with probability 0.001, the chance that all test cases do not fail is 0.999^(32*22) = 0.49442874426
886                 ZipfDistribution distribution = new ZipfDistribution(numPoints, exponent);
887                 randomData.setSeed(1001);
888                 double[] expectedCounts = new double[numPoints];
889                 long[] observedCounts = new long[numPoints];
890                 for (int i = 0; i < numPoints; i++) {
891                     expectedCounts[i] = sampleSize * (weights[i]/weightSum);
892                 }
893                 int[] sample = randomData.nextDeviates(distribution,sampleSize);
894                 for (int s : sample) {
895                     observedCounts[s-1]++;
896                 }
897                 UnitTestUtils.customAssertChiSquareAccept(expectedCounts, observedCounts, 0.001);
898             }
899         }
900     }
901 
902     @Test
903     void testNextSampleWithReplacement() {
904         final int sampleSize = 1000;
905         final double[] weights = {1, 2, 3, 4};
906         final int[] sample = randomData.nextSampleWithReplacement(sampleSize, weights);
907         final double[] expected = {sampleSize/10d, sampleSize/5d, 3*sampleSize/10d, 2*sampleSize/5d};
908         final long[] observed = {0, 0, 0, 0};
909         for (int i = 0; i < sampleSize; i++) {
910             observed[sample[i]]++;
911         }
912         UnitTestUtils.customAssertChiSquareAccept(new String[] { "0", "1", "2", "3"}, expected, observed, 0.01);
913     }
914 
915     @Test
916     void testNextSampleWithReplacementPointMass() {
917         final int sampleSize = 2;
918         double[] weights = {1};
919         final int[] expected = new int[] {0, 0};
920         UnitTestUtils.customAssertEquals(expected, randomData.nextSampleWithReplacement(sampleSize, weights));
921         weights = new double[] {1, 0};
922         UnitTestUtils.customAssertEquals(expected, randomData.nextSampleWithReplacement(sampleSize, weights));
923     }
924 
925     @Test
926     void testNextSampleWithReplacementAllZeroWeights() {
927         assertThrows(MathIllegalArgumentException.class, () -> {
928             final double[] weights = {0, 0, 0};
929             randomData.nextSampleWithReplacement(1, weights);
930         });
931     }
932 
933     @Test
934     void testNextSampleWithReplacementNegativeWeights() {
935         assertThrows(MathIllegalArgumentException.class, () -> {
936             final double[] weights = {-1, 1, 0};
937             randomData.nextSampleWithReplacement(1, weights);
938         });
939     }
940 
941     @Test
942     void testNextSampleWithReplacement0SampleSize() {
943         final double[] weights = {1, 0};
944         final int[] expected = {};
945         UnitTestUtils.customAssertEquals(expected, randomData.nextSampleWithReplacement(0, weights));
946     }
947 
948     @Test
949     void testNextSampleWithReplacementNegativeSampleSize() {
950         assertThrows(MathIllegalArgumentException.class, () -> {
951             final double[] weights = {1, 0};
952             randomData.nextSampleWithReplacement(-1, weights);
953         });
954     }
955 
956     @Test
957     void testNextSampleWithReplacementNaNWeights() {
958         assertThrows(MathIllegalArgumentException.class, () -> {
959             final double[] weights = {1, Double.NaN};
960             randomData.nextSampleWithReplacement(0, weights);
961         });
962     }
963 
964     @Test
965     void testNextDeviateEnumeratedIntegerDistribution() {
966         final int sampleSize = 1000;
967         final int[] data = new int[] {0, 1, 1, 2, 2, 2};
968         final EnumeratedIntegerDistribution dist = new EnumeratedIntegerDistribution(data);
969         final int[] sample = randomData.nextDeviates(dist, sampleSize);
970         final double[] expected = {sampleSize/6d, sampleSize/3d, sampleSize/2d};
971         final long[] observed = {0, 0, 0};
972         for (int i = 0; i < sampleSize; i++) {
973             observed[sample[i]]++;
974         }
975         UnitTestUtils.customAssertChiSquareAccept(new String[] { "0", "1", "2"}, expected, observed, 0.01);
976     }
977 
978     @Test
979     void testNextDeviateEnumeratedRealDistribution() {
980         final int sampleSize = 1000;
981         final double[] data = new double[] {0, 1, 1, 2, 2, 2};
982         final EnumeratedRealDistribution dist = new EnumeratedRealDistribution(data);
983         final double[] sample = randomData.nextDeviates(dist, sampleSize);
984         final double[] expected = {sampleSize/6d, sampleSize/3d, sampleSize/2d};
985         final long[] observed = {0, 0, 0};
986         for (int i = 0; i < sampleSize; i++) {
987             observed[(int)sample[i]]++;
988         }
989         UnitTestUtils.customAssertChiSquareAccept(new String[] { "0", "1", "2"}, expected, observed, 0.01);
990     }
991 
992 }