    1   /*
    2    * Copyright 2003-2004 The Apache Software Foundation.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.math.random;
    17
    18  import junit.framework.Test;
    19  import junit.framework.TestSuite;
    20  import java.security.NoSuchProviderException;
    21  import java.security.NoSuchAlgorithmException;
    22  import java.util.HashSet;
    23
    24  import org.apache.commons.math.RetryTestCase;
    25  import org.apache.commons.math.stat.Frequency;
    26  import org.apache.commons.math.stat.inference.ChiSquareTestImpl;
    27  import org.apache.commons.math.stat.descriptive.SummaryStatistics;
    28
    29  /***
    30   * Test cases for the RandomData class.
    31   *
    32   * @version $Revision: 1.17 $ $Date: 2004/10/08 05:08:19 $
    33   */
    34
    35  public final class RandomDataTest extends RetryTestCase {
    36
    37      public RandomDataTest(String name) {
    38          super(name);
    39      }
    40
    41      private long smallSampleSize = 1000;
    42      private double[] expected = {250,250,250,250};
    43      private int largeSampleSize = 10000;
    44      private int tolerance = 50;
    45      private String[] hex =
    46          {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"};
    47      private RandomDataImpl randomData = new RandomDataImpl();
    48      private ChiSquareTestImpl testStatistic = new ChiSquareTestImpl();
    49
    50      public void setUp() {
    51      }
    52
    53      public static Test suite() {
    54          TestSuite suite = new TestSuite(RandomDataTest.class);
    55          suite.setName("RandomData Tests");
    56          return suite;
    57      }
    58
    59      /*** test dispersion and failure modes for nextInt() */
    60      public void testNextInt() {
    61          try {
    62              int x = randomData.nextInt(4,3);
    63              fail("IllegalArgumentException expected");
    64          } catch (IllegalArgumentException ex) {
    65              ;
    66          }
    67          Frequency freq = new Frequency();
    68          int value = 0;
    69          for (int i=0;i<smallSampleSize;i++) {
    70              value = randomData.nextInt(0,3);
    71              assertTrue("nextInt range",(value >= 0) && (value <= 3));
    72              freq.addValue(value);
    73          }
    74          long[] observed = new long[4];
    75          for (int i=0; i<4; i++) {
    76              observed[i] = freq.getCount(i);
    77          }
    78
    79          /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
    80           * Change to 11.34 for alpha = .01
    81           */
    82          assertTrue("chi-square test -- will fail about 1 in 1000 times",
    83              testStatistic.chiSquare(expected,observed) < 16.27);
    84      }
    85
    86      /*** test dispersion and failure modes for nextLong() */
    87      public void testNextLong() {
    88         try {
    89              long x = randomData.nextLong(4,3);
    90              fail("IllegalArgumentException expected");
    91          } catch (IllegalArgumentException ex) {
    92              ;
    93          }
    94         Frequency freq = new Frequency();
    95         long value = 0;
    96          for (int i=0;i<smallSampleSize;i++) {
    97              value = randomData.nextLong(0,3);
    98              assertTrue("nextInt range",(value >= 0) && (value <= 3));
    99              freq.addValue(value);
    100         }
    101         long[] observed = new long[4];
    102         for (int i=0; i<4; i++) {
    103             observed[i] = freq.getCount(i);
    104         }
    105
    106         /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
    107          * Change to 11.34 for alpha = .01
    108          */
    109         assertTrue("chi-square test -- will fail about 1 in 1000 times",
    110             testStatistic.chiSquare(expected,observed) < 16.27);
    111     }
    112
    113     /*** test dispersion and failure modes for nextSecureLong() */
    114     public void testNextSecureLong() {
    115         try {
    116             long x = randomData.nextSecureLong(4,3);
    117             fail("IllegalArgumentException expected");
    118         } catch (IllegalArgumentException ex) {
    119             ;
    120         }
    121         Frequency freq = new Frequency();
    122         long value = 0;
    123         for (int i=0;i<smallSampleSize;i++) {
    124             value = randomData.nextSecureLong(0,3);
    125             assertTrue("nextInt range",(value >= 0) && (value <= 3));
    126             freq.addValue(value);
    127         }
    128         long[] observed = new long[4];
    129         for (int i=0; i<4; i++) {
    130             observed[i] = freq.getCount(i);
    131         }
    132
    133         /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
    134          * Change to 11.34 for alpha = .01
    135          */
    136         assertTrue("chi-square test -- will fail about 1 in 1000 times",
    137             testStatistic.chiSquare(expected,observed) < 16.27);
    138     }
    139
    140     /*** test dispersion and failure modes for nextSecureInt() */
    141     public void testNextSecureInt() {
    142         try {
    143             long x = randomData.nextSecureInt(4,3);
    144             fail("IllegalArgumentException expected");
    145         } catch (IllegalArgumentException ex) {
    146             ;
    147         }
    148         Frequency freq = new Frequency();
    149         int value = 0;
    150         for (int i=0;i<smallSampleSize;i++) {
    151             value = randomData.nextSecureInt(0,3);
    152             assertTrue("nextInt range",(value >= 0) && (value <= 3));
    153             freq.addValue(value);
    154         }
    155         long[] observed = new long[4];
    156         for (int i=0; i<4; i++) {
    157             observed[i] = freq.getCount(i);
    158         }
    159
    160         /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
    161          * Change to 11.34 for alpha = .01
    162          */
    163         assertTrue("chi-square test -- will fail about 1 in 1000 times",
    164             testStatistic.chiSquare(expected,observed) < 16.27);
    165     }
    166
    167     /***
    168      * Make sure that empirical distribution of random Poisson(4)'s
    169      * has P(X <= 5) close to actual cumulative Poisson probablity
    170      * and that nextPoisson fails when mean is non-positive
    171      * TODO: replace with statistical test, adding test stat to TestStatistic
    172      */
    173     public void testNextPoisson() {
    174         try {
    175             long x = randomData.nextPoisson(0);
    176             fail("zero mean -- expecting IllegalArgumentException");
    177         } catch (IllegalArgumentException ex) {
    178             ;
    179         }
    180         Frequency f = new Frequency();
    181         long v = 0;
    182         for (int i = 0; i<largeSampleSize; i++) {
    183             try {
    184                 f.addValue(randomData.nextPoisson(4.0d));
    185             } catch (Exception ex) {
    186                 fail(ex.getMessage());
    187             }
    188         }
    189         long cumFreq = f.getCount(0) + f.getCount(1) + f.getCount(2) +
    190                         f.getCount(3) + f.getCount(4) + f.getCount(5);
    191         long sumFreq = f.getSumFreq();
    192         double cumPct =
    193             new Double(cumFreq).doubleValue()/new Double(sumFreq).doubleValue();
    194         assertEquals("cum Poisson(4)",cumPct,0.7851,0.2);
    195         try {
    196             long x = randomData.nextPoisson(-1);
    197             fail("negative mean supplied -- IllegalArgumentException expected");
    198         } catch (IllegalArgumentException ex) {
    199             ;
    200         }
    201         try {
    202             long x = randomData.nextPoisson(0);
    203             fail("0 mean supplied -- IllegalArgumentException expected");
    204         } catch (IllegalArgumentException ex) {
    205             ;
    206         }
    207
    208     }
    209
    210     /*** test dispersion and failute modes for nextHex() */
    211     public void testNextHex() {
    212         try {
    213             String x = randomData.nextHexString(-1);
    214             fail("negative length supplied -- IllegalArgumentException expected");
    215         } catch (IllegalArgumentException ex) {
    216             ;
    217         }
    218         try {
    219             String x = randomData.nextHexString(0);
    220             fail("zero length supplied -- IllegalArgumentException expected");
    221         } catch (IllegalArgumentException ex) {
    222             ;
    223         }
    224         String hexString = randomData.nextHexString(3);
    225         if (hexString.length() != 3) {
    226                 fail("incorrect length for generated string");
    227         }
    228         hexString = randomData.nextHexString(1);
    229         if (hexString.length() != 1) {
    230                 fail("incorrect length for generated string");
    231         }
    232         try {
    233             hexString = randomData.nextHexString(0);
    234             fail("zero length requested -- expecting IllegalArgumentException");
    235         } catch (IllegalArgumentException ex) {
    236             ;
    237         }
    238         if (hexString.length() != 1) {
    239                 fail("incorrect length for generated string");
    240         }
    241         Frequency f = new Frequency();
    242         for (int i = 0; i < smallSampleSize; i++) {
    243             hexString = randomData.nextHexString(100);
    244             if (hexString.length() != 100) {
    245                 fail("incorrect length for generated string");
    246             }
    247             for (int j = 0; j < hexString.length(); j++) {
    248                 f.addValue(hexString.substring(j,j+1));
    249             }
    250         }
    251         double[] expected = new double[16];
    252         long[] observed = new long[16];
    253         for (int i = 0; i < 16; i++) {
    254             expected[i] = (double)smallSampleSize*100/(double)16;
    255             observed[i] = f.getCount(hex[i]);
    256         }
    257         /* Use ChiSquare dist with df = 16-1 = 15, alpha = .001
    258          * Change to 30.58 for alpha = .01
    259          */
    260         assertTrue("chi-square test -- will fail about 1 in 1000 times",
    261             testStatistic.chiSquare(expected,observed) < 37.70);
    262     }
    263
    264     /*** test dispersion and failute modes for nextHex() */
    265     public void testNextSecureHex() {
    266         try {
    267             String x = randomData.nextSecureHexString(-1);
    268             fail("negative length -- IllegalArgumentException expected");
    269         } catch (IllegalArgumentException ex) {
    270             ;
    271         }
    272         try {
    273             String x = randomData.nextSecureHexString(0);
    274             fail("zero length -- IllegalArgumentException expected");
    275         } catch (IllegalArgumentException ex) {
    276             ;
    277         }
    278         String hexString = randomData.nextSecureHexString(3);
    279         if (hexString.length() != 3) {
    280                 fail("incorrect length for generated string");
    281         }
    282         hexString = randomData.nextSecureHexString(1);
    283         if (hexString.length() != 1) {
    284                 fail("incorrect length for generated string");
    285         }
    286         try {
    287             hexString = randomData.nextSecureHexString(0);
    288             fail("zero length requested -- expecting IllegalArgumentException");
    289         } catch (IllegalArgumentException ex) {
    290             ;
    291         }
    292         if (hexString.length() != 1) {
    293                 fail("incorrect length for generated string");
    294         }
    295         Frequency f = new Frequency();
    296         for (int i = 0; i < smallSampleSize; i++) {
    297             hexString = randomData.nextSecureHexString(100);
    298             if (hexString.length() != 100) {
    299                 fail("incorrect length for generated string");
    300             }
    301             for (int j = 0; j < hexString.length(); j++) {
    302                 f.addValue(hexString.substring(j,j+1));
    303             }
    304         }
    305         double[] expected = new double[16];
    306         long[] observed = new long[16];
    307         for (int i = 0; i < 16; i++) {
    308             expected[i] = (double)smallSampleSize*100/(double)16;
    309             observed[i] = f.getCount(hex[i]);
    310         }
    311         /* Use ChiSquare dist with df = 16-1 = 15, alpha = .001
    312          * Change to 30.58 for alpha = .01
    313          */
    314         assertTrue("chi-square test -- will fail about 1 in 1000 times",
    315             testStatistic.chiSquare(expected,observed) < 37.70);
    316     }
    317
    318     /*** test failure modes and dispersion of nextUniform() */
    319     public void testNextUniform() {
    320         try {
    321             double x = randomData.nextUniform(4,3);
    322             fail("IllegalArgumentException expected");
    323         } catch (IllegalArgumentException ex) {
    324             ;
    325         }
    326         try {
    327             double x = randomData.nextUniform(3,3);
    328             fail("IllegalArgumentException expected");
    329         } catch (IllegalArgumentException ex) {
    330             ;
    331         }
    332         double[] expected = {500,500};
    333         long[] observed = {0,0};
    334         double lower = -1d;
    335         double upper = 20d;
    336         double midpoint = (lower + upper)/2d;
    337         double result = 0;
    338         for (int i = 0; i < 1000; i++) {
    339             result = randomData.nextUniform(lower,upper);
    340             if ((result == lower) || (result == upper)) {
    341                 fail("generated value equal to an endpoint: " + result);
    342             }
    343             if (result < midpoint) {
    344                 observed[0]++;
    345             } else {
    346                 observed[1]++;
    347             }
    348         }
    349         /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
    350          * Change to 6.64 for alpha = .01
    351          */
    352         assertTrue("chi-square test -- will fail about 1 in 1000 times",
    353             testStatistic.chiSquare(expected,observed) < 10.83);
    354     }
    355
    356     /*** test failure modes and distribution of nextGaussian() */
    357     public void testNextGaussian() {
    358         try {
    359             double x = randomData.nextGaussian(0,0);
    360             fail("zero sigma -- IllegalArgumentException expected");
    361         } catch (IllegalArgumentException ex) {
    362             ;
    363         }
    364         SummaryStatistics u = SummaryStatistics.newInstance();
    365         for (int i = 0; i<largeSampleSize; i++) {
    366             u.addValue(randomData.nextGaussian(0,1));
    367         }
    368         double xbar = u.getMean();
    369         double s = u.getStandardDeviation();
    370         double n = (double) u.getN();
    371         /* t-test at .001-level TODO: replace with externalized t-test, with
    372          * test statistic defined in TestStatistic
    373          */
    374         assertTrue(Math.abs(xbar)/(s/Math.sqrt(n))< 3.29);
    375     }
    376
    377     /*** test failure modes and distribution of nextExponential() */
    378     public void testNextExponential() {
    379         try {
    380             double x = randomData.nextExponential(-1);
    381             fail("negative mean -- expecting IllegalArgumentException");
    382         } catch (IllegalArgumentException ex) {
    383             ;
    384         }
    385         assertEquals("0 mean", 0,randomData.nextExponential(0),10E-8);
    386         long cumFreq = 0;
    387         double v = 0;
    388         for (int i = 0; i < largeSampleSize; i++) {
    389             v = randomData.nextExponential(1);
    390             assertTrue("exponential deviate postive", v > 0);
    391             if (v < 2) cumFreq++;
    392         }
    393         /* TODO: Replace with a statistical test, with statistic added to
    394          * TestStatistic.  Check below compares observed cumulative distribution
    395          * evaluated at 2 with exponential CDF
    396          */
    397         assertEquals("exponential cumulative distribution",
    398             (double)cumFreq/(double)largeSampleSize,0.8646647167633873,.2);
    399     }
    400
    401     /*** test reseeding, algorithm/provider games */
    402     public void testConfig() throws NoSuchProviderException,
    403       NoSuchAlgorithmException{
    404         randomData.reSeed(1000);
    405         double v = randomData.nextUniform(0,1);
    406         randomData.reSeed();
    407         assertTrue("different seeds",
    408             Math.abs(v - randomData.nextUniform(0,1)) > 10E-12);
    409         randomData.reSeed(1000);
    410         assertEquals("same seeds",v,randomData.nextUniform(0,1),10E-12);
    411         randomData.reSeedSecure(1000);
    412         String hex = randomData.nextSecureHexString(40);
    413         randomData.reSeedSecure();
    414         assertTrue("different seeds",
    415             !hex.equals(randomData.nextSecureHexString(40)));
    416         randomData.reSeedSecure(1000);
    417         assertTrue("same seeds",
    418             !hex.equals(randomData.nextSecureHexString(40)));
    419
    420         /* remove this test back soon,
    421          * since it takes about 4 seconds */
    422
    423         randomData.setSecureAlgorithm("SHA1PRNG","SUN");
    424         assertTrue("different seeds",
    425             !hex.equals(randomData.nextSecureHexString(40)));
    426         try {
    427             randomData.setSecureAlgorithm("NOSUCHTHING","SUN");
    428             fail("expecting NoSuchAlgorithmException");
    429         } catch (NoSuchAlgorithmException ex) {
    430             ;
    431         }
    432
    433         try {
    434             randomData.setSecureAlgorithm("SHA1PRNG","NOSUCHPROVIDER");
    435             fail("expecting NoSuchProviderException");
    436         } catch (NoSuchProviderException ex) {
    437             ;
    438         }
    439
    440         // test reseeding without first using the generators
    441         RandomDataImpl rd = new RandomDataImpl();
    442         rd.reSeed(100);
    443         double ret = rd.nextLong(1,2);
    444         RandomDataImpl rd2 = new RandomDataImpl();
    445         rd2.reSeedSecure(2000);
    446         ret = rd2.nextSecureLong(1,2);
    447         rd = new RandomDataImpl();
    448         rd.reSeed();
    449         ret = rd.nextLong(1,2);
    450         rd2 = new RandomDataImpl();
    451         rd2.reSeedSecure();
    452         ret = rd2.nextSecureLong(1,2);
    453     }
    454
    455     /*** tests for nextSample() sampling from Collection */
    456     public void testNextSample() {
    457        Object[][] c = {{"0","1"},{"0","2"},{"0","3"},{"0","4"},{"1","2"},
    458                         {"1","3"},{"1","4"},{"2","3"},{"2","4"},{"3","4"}};
    459        long[] observed = {0,0,0,0,0,0,0,0,0,0};
    460        double[] expected = {100,100,100,100,100,100,100,100,100,100};
    461
    462        HashSet cPop = new HashSet();  //{0,1,2,3,4}
    463        for (int i = 0; i < 5; i++) {
    464            cPop.add(Integer.toString(i));
    465        }
    466
    467        Object[] sets = new Object[10]; // 2-sets from 5
    468        for (int i = 0; i < 10; i ++) {
    469            HashSet hs = new HashSet();
    470            hs.add(c[i][0]);
    471            hs.add(c[i][1]);
    472            sets[i] = hs;
    473        }
    474
    475        for (int i = 0; i < 1000; i ++) {
    476            Object[] cSamp = randomData.nextSample(cPop,2);
    477            observed[findSample(sets,cSamp)]++;
    478        }
    479
    480         /* Use ChiSquare dist with df = 10-1 = 9, alpha = .001
    481          * Change to 21.67 for alpha = .01
    482          */
    483         assertTrue("chi-square test -- will fail about 1 in 1000 times",
    484             testStatistic.chiSquare(expected,observed) < 27.88);
    485
    486        // Make sure sample of size = size of collection returns same collection
    487        HashSet hs = new HashSet();
    488        hs.add("one");
    489        Object[] one = randomData.nextSample(hs,1);
    490        String oneString = (String) one[0];
    491        if ((one.length != 1) || !oneString.equals("one")){
    492            fail("bad sample for set size = 1, sample size = 1");
    493        }
    494
    495        // Make sure we fail for sample size > collection size
    496        try {
    497            one = randomData.nextSample(hs,2);
    498            fail("sample size > set size, expecting IllegalArgumentException");
    499        } catch (IllegalArgumentException ex) {
    500            ;
    501        }
    502
    503        // Make sure we fail for empty collection
    504        try {
    505            hs = new HashSet();
    506            one = randomData.nextSample(hs,0);
    507            fail("n = k = 0, expecting IllegalArgumentException");
    508        } catch (IllegalArgumentException ex) {
    509            ;
    510        }
    511     }
    512
    513     private int findSample(Object[] u, Object[] samp) {
    514         int result = -1;
    515         for (int i = 0; i < u.length; i++) {
    516             HashSet set = (HashSet) u[i];
    517             HashSet sampSet = new HashSet();
    518             for (int j = 0; j < samp.length; j++) {
    519                 sampSet.add(samp[j]);
    520             }
    521             if (set.equals(sampSet)) {
    522                return i;
    523            }
    524         }
    525         fail("sample not found:{" + samp[0] + "," + samp[1] + "}");
    526         return -1;
    527     }
    528
    529     /*** tests for nextPermutation */
    530     public void testNextPermutation() {
    531         int[][] p = {{0,1,2},{0,2,1},{1,0,2},{1,2,0},{2,0,1},{2,1,0}};
    532         long[] observed = {0,0,0,0,0,0};
    533         double[] expected = {100,100,100,100,100,100};
    534
    535         for (int i = 0; i < 600; i++) {
    536             int[] perm = randomData.nextPermutation(3,3);
    537             observed[findPerm(p,perm)]++;
    538         }
    539
    540         /* Use ChiSquare dist with df = 6-1 = 5, alpha = .001
    541          * Change to 15.09 for alpha = .01
    542          */
    543         assertTrue("chi-square test -- will fail about 1 in 1000 times",
    544                 testStatistic.chiSquare(expected,observed) < 20.52);
    545
    546         // Check size = 1 boundary case
    547         int[] perm = randomData.nextPermutation(1,1);
    548         if ((perm.length != 1) || (perm[0] != 0)){
    549             fail("bad permutation for n = 1, sample k = 1");
    550
    551             // Make sure we fail for k size > n
    552             try {
    553                 perm = randomData.nextPermutation(2,3);
    554                 fail("permutation k > n, expecting IllegalArgumentException");
    555             } catch (IllegalArgumentException ex) {
    556                 ;
    557             }
    558
    559             // Make sure we fail for n = 0
    560             try {
    561                 perm = randomData.nextPermutation(0,0);
    562                 fail("permutation k = n = 0, expecting IllegalArgumentException");
    563             } catch (IllegalArgumentException ex) {
    564                 ;
    565             }
    566         }
    567     }
    568
    569     private int findPerm(int[][] p, int[] samp) {
    570         int result = -1;
    571         for (int i = 0; i < p.length; i++) {
    572             boolean good = true;
    573             for (int j = 0; j < samp.length; j++) {
    574                 if (samp[j] != p[i][j]) {
    575                     good = false;
    576                 }
    577             }
    578             if (good)  {
    579                 return i;
    580             }
    581         }
    582         fail("permutation not found");
    583         return -1;
    584     }
    585 }
    586