001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.math.random;
018    
019    import junit.framework.Test;
020    import junit.framework.TestSuite;
021    import java.util.HashSet;
022    
023    import org.apache.commons.math.RetryTestCase;
024    import org.apache.commons.math.stat.Frequency;
025    import org.apache.commons.math.stat.inference.ChiSquareTestImpl;
026    import org.apache.commons.math.stat.descriptive.SummaryStatistics;
027    
028    /**
029     * Test cases for the RandomData class.
030     * 
031     * @version $Revision: 783041 $ $Date: 2009-04-05 11:55:59 -0500 (Sun, 05 Apr
032     *          2009) $
033     */
034    
035    public class RandomDataTest extends RetryTestCase {
036    
037            public RandomDataTest(String name) {
038                    super(name);
039                    randomData = new RandomDataImpl();
040            }
041    
042            protected long smallSampleSize = 1000;
043            protected double[] expected = { 250, 250, 250, 250 };
044            protected int largeSampleSize = 10000;
045            private String[] hex = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
046                            "a", "b", "c", "d", "e", "f" };
047            protected RandomDataImpl randomData = null;
048            protected ChiSquareTestImpl testStatistic = new ChiSquareTestImpl();
049    
050            public static Test suite() {
051                    TestSuite suite = new TestSuite(RandomDataTest.class);
052                    suite.setName("RandomData Tests");
053                    return suite;
054            }
055    
056            public void testNextIntExtremeValues() {
057                    int x = randomData.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
058                    int y = randomData.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
059                    assertFalse(x == y);
060            }
061    
062            public void testNextLongExtremeValues() {
063                    long x = randomData.nextLong(Long.MIN_VALUE, Long.MAX_VALUE);
064                    long y = randomData.nextLong(Long.MIN_VALUE, Long.MAX_VALUE);
065                    assertFalse(x == y);
066            }
067    
068            /** test dispersion and failure modes for nextInt() */
069            public void testNextInt() {
070                    try {
071                            randomData.nextInt(4, 3);
072                            fail("IllegalArgumentException expected");
073                    } catch (IllegalArgumentException ex) {
074                            // ignored
075                    }
076                    Frequency freq = new Frequency();
077                    int value = 0;
078                    for (int i = 0; i < smallSampleSize; i++) {
079                            value = randomData.nextInt(0, 3);
080                            assertTrue("nextInt range", (value >= 0) && (value <= 3));
081                            freq.addValue(value);
082                    }
083                    long[] observed = new long[4];
084                    for (int i = 0; i < 4; i++) {
085                            observed[i] = freq.getCount(i);
086                    }
087    
088                    /*
089                     * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34
090                     * for alpha = .01
091                     */
092                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
093                                    testStatistic.chiSquare(expected, observed) < 16.27);
094            }
095    
096            /** test dispersion and failure modes for nextLong() */
097            public void testNextLong() {
098                    try {
099                            randomData.nextLong(4, 3);
100                            fail("IllegalArgumentException expected");
101                    } catch (IllegalArgumentException ex) {
102                            // ignored
103                    }
104                    Frequency freq = new Frequency();
105                    long value = 0;
106                    for (int i = 0; i < smallSampleSize; i++) {
107                            value = randomData.nextLong(0, 3);
108                            assertTrue("nextInt range", (value >= 0) && (value <= 3));
109                            freq.addValue(value);
110                    }
111                    long[] observed = new long[4];
112                    for (int i = 0; i < 4; i++) {
113                            observed[i] = freq.getCount(i);
114                    }
115    
116                    /*
117                     * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34
118                     * for alpha = .01
119                     */
120                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
121                                    testStatistic.chiSquare(expected, observed) < 16.27);
122            }
123    
124            /** test dispersion and failure modes for nextSecureLong() */
125            public void testNextSecureLong() {
126                    try {
127                            randomData.nextSecureLong(4, 3);
128                            fail("IllegalArgumentException expected");
129                    } catch (IllegalArgumentException ex) {
130                            // ignored
131                    }
132                    Frequency freq = new Frequency();
133                    long value = 0;
134                    for (int i = 0; i < smallSampleSize; i++) {
135                            value = randomData.nextSecureLong(0, 3);
136                            assertTrue("nextInt range", (value >= 0) && (value <= 3));
137                            freq.addValue(value);
138                    }
139                    long[] observed = new long[4];
140                    for (int i = 0; i < 4; i++) {
141                            observed[i] = freq.getCount(i);
142                    }
143    
144                    /*
145                     * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34
146                     * for alpha = .01
147                     */
148                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
149                                    testStatistic.chiSquare(expected, observed) < 16.27);
150            }
151    
152            /** test dispersion and failure modes for nextSecureInt() */
153            public void testNextSecureInt() {
154                    try {
155                            randomData.nextSecureInt(4, 3);
156                            fail("IllegalArgumentException expected");
157                    } catch (IllegalArgumentException ex) {
158                            // ignored
159                    }
160                    Frequency freq = new Frequency();
161                    int value = 0;
162                    for (int i = 0; i < smallSampleSize; i++) {
163                            value = randomData.nextSecureInt(0, 3);
164                            assertTrue("nextInt range", (value >= 0) && (value <= 3));
165                            freq.addValue(value);
166                    }
167                    long[] observed = new long[4];
168                    for (int i = 0; i < 4; i++) {
169                            observed[i] = freq.getCount(i);
170                    }
171    
172                    /*
173                     * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34
174                     * for alpha = .01
175                     */
176                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
177                                    testStatistic.chiSquare(expected, observed) < 16.27);
178            }
179    
180            /**
181             * Make sure that empirical distribution of random Poisson(4)'s has P(X <=
182             * 5) close to actual cumulative Poisson probablity and that nextPoisson
183             * fails when mean is non-positive TODO: replace with statistical test,
184             * adding test stat to TestStatistic
185             */
186            public void testNextPoisson() {
187                    try {
188                            randomData.nextPoisson(0);
189                            fail("zero mean -- expecting IllegalArgumentException");
190                    } catch (IllegalArgumentException ex) {
191                            // ignored
192                    }
193                    Frequency f = new Frequency();
194                    for (int i = 0; i < largeSampleSize; i++) {
195                            try {
196                                    f.addValue(randomData.nextPoisson(4.0d));
197                            } catch (Exception ex) {
198                                    fail(ex.getMessage());
199                            }
200                    }
201                    long cumFreq = f.getCount(0) + f.getCount(1) + f.getCount(2)
202                                    + f.getCount(3) + f.getCount(4) + f.getCount(5);
203                    long sumFreq = f.getSumFreq();
204                    double cumPct = Double.valueOf(cumFreq).doubleValue()
205                                    / Double.valueOf(sumFreq).doubleValue();
206                    assertEquals("cum Poisson(4)", cumPct, 0.7851, 0.2);
207                    try {
208                            randomData.nextPoisson(-1);
209                            fail("negative mean supplied -- IllegalArgumentException expected");
210                    } catch (IllegalArgumentException ex) {
211                            // ignored
212                    }
213                    try {
214                            randomData.nextPoisson(0);
215                            fail("0 mean supplied -- IllegalArgumentException expected");
216                    } catch (IllegalArgumentException ex) {
217                            // ignored
218                    }
219    
220            }
221    
222            public void testNextPoissonLargeMean() {
223                    for (int i = 0; i < 1000; i++) {
224                            long n = randomData.nextPoisson(1500.0);
225                            assertTrue(0 <= n);
226                    }
227            }
228    
229            /** test dispersion and failute modes for nextHex() */
230            public void testNextHex() {
231                    try {
232                            randomData.nextHexString(-1);
233                            fail("negative length supplied -- IllegalArgumentException expected");
234                    } catch (IllegalArgumentException ex) {
235                            // ignored
236                    }
237                    try {
238                            randomData.nextHexString(0);
239                            fail("zero length supplied -- IllegalArgumentException expected");
240                    } catch (IllegalArgumentException ex) {
241                            // ignored
242                    }
243                    String hexString = randomData.nextHexString(3);
244                    if (hexString.length() != 3) {
245                            fail("incorrect length for generated string");
246                    }
247                    hexString = randomData.nextHexString(1);
248                    if (hexString.length() != 1) {
249                            fail("incorrect length for generated string");
250                    }
251                    try {
252                            hexString = randomData.nextHexString(0);
253                            fail("zero length requested -- expecting IllegalArgumentException");
254                    } catch (IllegalArgumentException ex) {
255                            // ignored
256                    }
257                    if (hexString.length() != 1) {
258                            fail("incorrect length for generated string");
259                    }
260                    Frequency f = new Frequency();
261                    for (int i = 0; i < smallSampleSize; i++) {
262                            hexString = randomData.nextHexString(100);
263                            if (hexString.length() != 100) {
264                                    fail("incorrect length for generated string");
265                            }
266                            for (int j = 0; j < hexString.length(); j++) {
267                                    f.addValue(hexString.substring(j, j + 1));
268                            }
269                    }
270                    double[] expected = new double[16];
271                    long[] observed = new long[16];
272                    for (int i = 0; i < 16; i++) {
273                            expected[i] = (double) smallSampleSize * 100 / 16;
274                            observed[i] = f.getCount(hex[i]);
275                    }
276                    /*
277                     * Use ChiSquare dist with df = 16-1 = 15, alpha = .001 Change to 30.58
278                     * for alpha = .01
279                     */
280                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
281                                    testStatistic.chiSquare(expected, observed) < 37.70);
282            }
283    
284            /** test dispersion and failute modes for nextHex() */
285            public void testNextSecureHex() {
286                    try {
287                            randomData.nextSecureHexString(-1);
288                            fail("negative length -- IllegalArgumentException expected");
289                    } catch (IllegalArgumentException ex) {
290                            // ignored
291                    }
292                    try {
293                            randomData.nextSecureHexString(0);
294                            fail("zero length -- IllegalArgumentException expected");
295                    } catch (IllegalArgumentException ex) {
296                            // ignored
297                    }
298                    String hexString = randomData.nextSecureHexString(3);
299                    if (hexString.length() != 3) {
300                            fail("incorrect length for generated string");
301                    }
302                    hexString = randomData.nextSecureHexString(1);
303                    if (hexString.length() != 1) {
304                            fail("incorrect length for generated string");
305                    }
306                    try {
307                            hexString = randomData.nextSecureHexString(0);
308                            fail("zero length requested -- expecting IllegalArgumentException");
309                    } catch (IllegalArgumentException ex) {
310                            // ignored
311                    }
312                    if (hexString.length() != 1) {
313                            fail("incorrect length for generated string");
314                    }
315                    Frequency f = new Frequency();
316                    for (int i = 0; i < smallSampleSize; i++) {
317                            hexString = randomData.nextSecureHexString(100);
318                            if (hexString.length() != 100) {
319                                    fail("incorrect length for generated string");
320                            }
321                            for (int j = 0; j < hexString.length(); j++) {
322                                    f.addValue(hexString.substring(j, j + 1));
323                            }
324                    }
325                    double[] expected = new double[16];
326                    long[] observed = new long[16];
327                    for (int i = 0; i < 16; i++) {
328                            expected[i] = (double) smallSampleSize * 100 / 16;
329                            observed[i] = f.getCount(hex[i]);
330                    }
331                    /*
332                     * Use ChiSquare dist with df = 16-1 = 15, alpha = .001 Change to 30.58
333                     * for alpha = .01
334                     */
335                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
336                                    testStatistic.chiSquare(expected, observed) < 37.70);
337            }
338    
339            /** test failure modes and dispersion of nextUniform() */
340            public void testNextUniform() {
341                    try {
342                            randomData.nextUniform(4, 3);
343                            fail("IllegalArgumentException expected");
344                    } catch (IllegalArgumentException ex) {
345                            // ignored
346                    }
347                    try {
348                            randomData.nextUniform(3, 3);
349                            fail("IllegalArgumentException expected");
350                    } catch (IllegalArgumentException ex) {
351                            // ignored
352                    }
353                    double[] expected = { 500, 500 };
354                    long[] observed = { 0, 0 };
355                    double lower = -1d;
356                    double upper = 20d;
357                    double midpoint = (lower + upper) / 2d;
358                    double result = 0;
359                    for (int i = 0; i < 1000; i++) {
360                            result = randomData.nextUniform(lower, upper);
361                            if ((result == lower) || (result == upper)) {
362                                    fail("generated value equal to an endpoint: " + result);
363                            }
364                            if (result < midpoint) {
365                                    observed[0]++;
366                            } else {
367                                    observed[1]++;
368                            }
369                    }
370                    /*
371                     * Use ChiSquare dist with df = 2-1 = 1, alpha = .001 Change to 6.64 for
372                     * alpha = .01
373                     */
374                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
375                                    testStatistic.chiSquare(expected, observed) < 10.83);
376            }
377    
378            /** test exclusive endpoints of nextUniform **/
379            public void testNextUniformExclusiveEndpoints() {
380                    for (int i = 0; i < 1000; i++) {
381                            double u = randomData.nextUniform(0.99, 1);
382                            assertTrue(u > 0.99 && u < 1);
383                    }
384            }
385    
386            /** test failure modes and distribution of nextGaussian() */
387            public void testNextGaussian() {
388                    try {
389                            randomData.nextGaussian(0, 0);
390                            fail("zero sigma -- IllegalArgumentException expected");
391                    } catch (IllegalArgumentException ex) {
392                            // ignored
393                    }
394                    SummaryStatistics u = new SummaryStatistics();
395                    for (int i = 0; i < largeSampleSize; i++) {
396                            u.addValue(randomData.nextGaussian(0, 1));
397                    }
398                    double xbar = u.getMean();
399                    double s = u.getStandardDeviation();
400                    double n = u.getN();
401                    /*
402                     * t-test at .001-level TODO: replace with externalized t-test, with
403                     * test statistic defined in TestStatistic
404                     */
405                    assertTrue(Math.abs(xbar) / (s / Math.sqrt(n)) < 3.29);
406            }
407    
408            /** test failure modes and distribution of nextExponential() */
409            public void testNextExponential() {
410                    try {
411                            randomData.nextExponential(-1);
412                            fail("negative mean -- expecting IllegalArgumentException");
413                    } catch (IllegalArgumentException ex) {
414                            // ignored
415                    }
416                    assertEquals("0 mean", 0, randomData.nextExponential(0), 10E-8);
417                    long cumFreq = 0;
418                    double v = 0;
419                    for (int i = 0; i < largeSampleSize; i++) {
420                            v = randomData.nextExponential(1);
421                            assertTrue("exponential deviate postive", v > 0);
422                            if (v < 2)
423                                    cumFreq++;
424                    }
425                    /*
426                     * TODO: Replace with a statistical test, with statistic added to
427                     * TestStatistic. Check below compares observed cumulative distribution
428                     * evaluated at 2 with exponential CDF
429                     */
430                    assertEquals("exponential cumulative distribution", (double) cumFreq
431                                    / (double) largeSampleSize, 0.8646647167633873, .2);
432            }
433    
434            /** test reseeding, algorithm/provider games */
435            public void testConfig() {
436                    randomData.reSeed(1000);
437                    double v = randomData.nextUniform(0, 1);
438                    randomData.reSeed();
439                    assertTrue("different seeds", Math
440                                    .abs(v - randomData.nextUniform(0, 1)) > 10E-12);
441                    randomData.reSeed(1000);
442                    assertEquals("same seeds", v, randomData.nextUniform(0, 1), 10E-12);
443                    randomData.reSeedSecure(1000);
444                    String hex = randomData.nextSecureHexString(40);
445                    randomData.reSeedSecure();
446                    assertTrue("different seeds", !hex.equals(randomData
447                                    .nextSecureHexString(40)));
448                    randomData.reSeedSecure(1000);
449                    assertTrue("same seeds", !hex
450                                    .equals(randomData.nextSecureHexString(40)));
451    
452                    /*
453                     * remove this test back soon, since it takes about 4 seconds
454                     * 
455                     * try { randomData.setSecureAlgorithm("SHA1PRNG","SUN"); } catch
456                     * (NoSuchProviderException ex) { ; } assertTrue("different seeds",
457                     * !hex.equals(randomData.nextSecureHexString(40))); try {
458                     * randomData.setSecureAlgorithm("NOSUCHTHING","SUN");
459                     * fail("expecting NoSuchAlgorithmException"); } catch
460                     * (NoSuchProviderException ex) { ; } catch (NoSuchAlgorithmException
461                     * ex) { ; }
462                     * 
463                     * try { randomData.setSecureAlgorithm("SHA1PRNG","NOSUCHPROVIDER");
464                     * fail("expecting NoSuchProviderException"); } catch
465                     * (NoSuchProviderException ex) { ; }
466                     */
467    
468                    // test reseeding without first using the generators
469                    RandomDataImpl rd = new RandomDataImpl();
470                    rd.reSeed(100);
471                    rd.nextLong(1, 2);
472                    RandomDataImpl rd2 = new RandomDataImpl();
473                    rd2.reSeedSecure(2000);
474                    rd2.nextSecureLong(1, 2);
475                    rd = new RandomDataImpl();
476                    rd.reSeed();
477                    rd.nextLong(1, 2);
478                    rd2 = new RandomDataImpl();
479                    rd2.reSeedSecure();
480                    rd2.nextSecureLong(1, 2);
481            }
482    
483            /** tests for nextSample() sampling from Collection */
484            public void testNextSample() {
485                    Object[][] c = { { "0", "1" }, { "0", "2" }, { "0", "3" },
486                                    { "0", "4" }, { "1", "2" }, { "1", "3" }, { "1", "4" },
487                                    { "2", "3" }, { "2", "4" }, { "3", "4" } };
488                    long[] observed = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
489                    double[] expected = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 };
490    
491                    HashSet<Object> cPop = new HashSet<Object>(); // {0,1,2,3,4}
492                    for (int i = 0; i < 5; i++) {
493                            cPop.add(Integer.toString(i));
494                    }
495    
496                    Object[] sets = new Object[10]; // 2-sets from 5
497                    for (int i = 0; i < 10; i++) {
498                            HashSet<Object> hs = new HashSet<Object>();
499                            hs.add(c[i][0]);
500                            hs.add(c[i][1]);
501                            sets[i] = hs;
502                    }
503    
504                    for (int i = 0; i < 1000; i++) {
505                            Object[] cSamp = randomData.nextSample(cPop, 2);
506                            observed[findSample(sets, cSamp)]++;
507                    }
508    
509                    /*
510                     * Use ChiSquare dist with df = 10-1 = 9, alpha = .001 Change to 21.67
511                     * for alpha = .01
512                     */
513                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
514                                    testStatistic.chiSquare(expected, observed) < 27.88);
515    
516                    // Make sure sample of size = size of collection returns same collection
517                    HashSet<Object> hs = new HashSet<Object>();
518                    hs.add("one");
519                    Object[] one = randomData.nextSample(hs, 1);
520                    String oneString = (String) one[0];
521                    if ((one.length != 1) || !oneString.equals("one")) {
522                            fail("bad sample for set size = 1, sample size = 1");
523                    }
524    
525                    // Make sure we fail for sample size > collection size
526                    try {
527                            one = randomData.nextSample(hs, 2);
528                            fail("sample size > set size, expecting IllegalArgumentException");
529                    } catch (IllegalArgumentException ex) {
530                            // ignored
531                    }
532    
533                    // Make sure we fail for empty collection
534                    try {
535                            hs = new HashSet<Object>();
536                            one = randomData.nextSample(hs, 0);
537                            fail("n = k = 0, expecting IllegalArgumentException");
538                    } catch (IllegalArgumentException ex) {
539                            // ignored
540                    }
541            }
542    
543            @SuppressWarnings("unchecked")
544            private int findSample(Object[] u, Object[] samp) {
545                    for (int i = 0; i < u.length; i++) {
546                            HashSet<Object> set = (HashSet<Object>) u[i];
547                            HashSet<Object> sampSet = new HashSet<Object>();
548                            for (int j = 0; j < samp.length; j++) {
549                                    sampSet.add(samp[j]);
550                            }
551                            if (set.equals(sampSet)) {
552                                    return i;
553                            }
554                    }
555                    fail("sample not found:{" + samp[0] + "," + samp[1] + "}");
556                    return -1;
557            }
558    
559            /** tests for nextPermutation */
560            public void testNextPermutation() {
561                    int[][] p = { { 0, 1, 2 }, { 0, 2, 1 }, { 1, 0, 2 }, { 1, 2, 0 },
562                                    { 2, 0, 1 }, { 2, 1, 0 } };
563                    long[] observed = { 0, 0, 0, 0, 0, 0 };
564                    double[] expected = { 100, 100, 100, 100, 100, 100 };
565    
566                    for (int i = 0; i < 600; i++) {
567                            int[] perm = randomData.nextPermutation(3, 3);
568                            observed[findPerm(p, perm)]++;
569                    }
570    
571                    /*
572                     * Use ChiSquare dist with df = 6-1 = 5, alpha = .001 Change to 15.09
573                     * for alpha = .01
574                     */
575                    assertTrue("chi-square test -- will fail about 1 in 1000 times",
576                                    testStatistic.chiSquare(expected, observed) < 20.52);
577    
578                    // Check size = 1 boundary case
579                    int[] perm = randomData.nextPermutation(1, 1);
580                    if ((perm.length != 1) || (perm[0] != 0)) {
581                            fail("bad permutation for n = 1, sample k = 1");
582    
583                            // Make sure we fail for k size > n
584                            try {
585                                    perm = randomData.nextPermutation(2, 3);
586                                    fail("permutation k > n, expecting IllegalArgumentException");
587                            } catch (IllegalArgumentException ex) {
588                                    // ignored
589                            }
590    
591                            // Make sure we fail for n = 0
592                            try {
593                                    perm = randomData.nextPermutation(0, 0);
594                                    fail("permutation k = n = 0, expecting IllegalArgumentException");
595                            } catch (IllegalArgumentException ex) {
596                                    // ignored
597                            }
598    
599                            // Make sure we fail for k < n < 0
600                            try {
601                                    perm = randomData.nextPermutation(-1, -3);
602                                    fail("permutation k < n < 0, expecting IllegalArgumentException");
603                            } catch (IllegalArgumentException ex) {
604                                    // ignored
605                            }
606    
607                    }
608            }
609            
610            // Disable until we have equals
611            //public void testSerial() {
612            //    assertEquals(randomData, TestUtils.serializeAndRecover(randomData));
613            //}
614            
615            private int findPerm(int[][] p, int[] samp) {
616                    for (int i = 0; i < p.length; i++) {
617                            boolean good = true;
618                            for (int j = 0; j < samp.length; j++) {
619                                    if (samp[j] != p[i][j]) {
620                                            good = false;
621                                    }
622                            }
623                            if (good) {
624                                    return i;
625                            }
626                    }
627                    fail("permutation not found");
628                    return -1;
629            }
630    }