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