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