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  *      http://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 package org.apache.commons.lang.math;
18 
19 import java.util.Random;
20 
21 import junit.framework.TestCase;
22 
23 /**
24  * Test cases for the {@link RandomUtils} class.
25  *
26  * @author <a href="mailto:phil@steitz.com">Phil Steitz</a>
27  * @version $Revision: 912374 $ $Date: 2010-02-21 17:20:57 +0000 (Sun, 21 Feb 2010) $
28  */
29 
30 public final class RandomUtilsTest extends TestCase {
31 
RandomUtilsTest(String name)32     public RandomUtilsTest(String name) {
33         super(name);
34     }
35 
36     /** test distribution of nextInt() */
testNextInt()37     public void testNextInt() {
38         tstNextInt(null);
39 
40         assertTrue (RandomUtils.nextInt() >= 0);
41     }
42 
43     /** test distribution of nextInt(Random) */
testNextInt2()44     public void testNextInt2() {
45         Random rnd = new Random();
46         rnd.setSeed(System.currentTimeMillis());
47         tstNextInt(rnd);
48     }
49 
50     /** test distribution of JVMRandom.nextInt() */
testJvmRandomNextInt()51     public void testJvmRandomNextInt() {
52         tstNextInt(RandomUtils.JVM_RANDOM);
53     }
54 
55 
56     /**
57      * Generate 1000 values for nextInt(bound) and compare
58      * the observed frequency counts to expected counts using
59      * a chi-square test.
60      * @param rnd Random to use if not null
61      */
tstNextInt(Random rnd)62     private void tstNextInt(Random rnd) {
63         int bound = 0;
64         int result = 0;
65         // test boundary condition: n = Integer.MAX_VALUE;
66         bound = Integer.MAX_VALUE;
67         if (rnd == null) {
68             result = RandomUtils.nextInt(bound);
69         } else {
70             result = RandomUtils.nextInt(rnd,bound);
71         }
72         assertTrue("result less than bound",result < bound);
73         assertTrue("result non-negative",result >= 0);
74 
75         // test uniformity -- use Chi-Square test at .01 level
76         bound = 4;
77         int[] expected = new int[] {250,250,250,250};
78         int[] observed = new int[] {0,0,0,0};
79         for (int i = 0; i < 1000; i ++) {
80             if (rnd == null) {
81                 result = RandomUtils.nextInt(bound);
82             } else {
83                 result = RandomUtils.nextInt(rnd,bound);
84             }
85             assertTrue(result < bound);
86             assertTrue(result >= 0);
87             observed[result]++;
88         }
89         /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
90          * Change to 11.34 for alpha = .01
91          */
92         assertTrue(
93             "chi-square test -- will fail about 1 in 1000 times",
94             chiSquare(expected,observed) < 16.27);
95     }
96 
97     /** test distribution of nextLong() */
testNextLong()98     public void testNextLong() {
99         tstNextLong(null);
100     }
101 
102     /** test distribution of nextLong(Random) BROKEN
103      *  contract of nextLong(Random) is different from
104      * nextLong() */
testNextLong2()105     public void testNextLong2() {
106         Random rnd = new Random();
107         rnd.setSeed(System.currentTimeMillis());
108         tstNextLong(rnd);
109     }
110 
111     /**
112      * Generate 1000 values for nextLong() and check that
113      * p(value < long.MAXVALUE/2) ~ 0.5. Use chi-square test
114      * with df = 2-1 = 1
115      * @param rnd Random to use if not null
116      */
tstNextLong(Random rnd)117     private void tstNextLong(Random rnd) {
118         // Distribution
119         int[] expected = new int[] {500,500};
120         int[] observed = new int[] {0,0};
121         // Even/Odd
122         int[] expected2 = new int[] {500,500};
123         int[] observed2 = new int[] {0,0};
124         long result = 0;
125         long midPoint = Long.MAX_VALUE/2;
126         for (int i = 0; i < 1000; i ++) {
127             if (rnd == null) {
128                 result = Math.abs(RandomUtils.nextLong());
129             } else {
130                 result = Math.abs(RandomUtils.nextLong(rnd));
131             }
132             if (result < midPoint) {
133                 observed[0]++;
134             } else {
135                 observed[1]++;
136             }
137             if (result % 2 == 0) {
138                observed2[0]++;
139             } else {
140                observed2[1]++;
141             }
142         }
143         /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
144          * Change to 6.64 for alpha = .01
145          */
146         assertTrue(
147             "mid point chi-square test -- will fail about 1 in 1000 times",
148             chiSquare(expected,observed) < 10.83);
149         assertTrue(
150                 "odd/even chi-square test -- will fail about 1 in 1000 times",
151                 chiSquare(expected2,observed2) < 10.83);
152     }
153 
154 
155     /** test distribution of nextBoolean() */
testNextBoolean()156     public void testNextBoolean() {
157         tstNextBoolean(null);
158     }
159 
160     /** test distribution of nextBoolean(Random) */
testNextBoolean2()161     public void testNextBoolean2() {
162         Random rnd = new Random();
163         rnd.setSeed(System.currentTimeMillis());
164         tstNextBoolean(rnd);
165     }
166 
167     /**
168      * Generate 1000 values for nextBoolean and check that
169      * p(value = false) ~ 0.5. Use chi-square test
170      * with df = 2-1 = 1
171      * @param rnd Random to use if not null
172      */
tstNextBoolean(Random rnd)173     private void tstNextBoolean(Random rnd) {
174         int[] expected = new int[] {500,500};
175         int[] observed = new int[] {0,0};
176         boolean result = false;
177         for (int i = 0; i < 1000; i ++) {
178             if (rnd == null) {
179                 result = RandomUtils.nextBoolean();
180             } else {
181                 result = RandomUtils.nextBoolean(rnd);
182             }
183             if (result) {
184                 observed[0]++;
185             } else {
186                 observed[1]++;
187             }
188         }
189         /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
190          * Change to 6.64 for alpha = .01
191          */
192         assertTrue(
193             "chi-square test -- will fail about 1 in 1000 times",
194             chiSquare(expected,observed) < 10.83 );
195     }
196 
197     /** test distribution of nextFloat() */
testNextFloat()198     public void testNextFloat() {
199         tstNextFloat(null);
200     }
201 
202     /** test distribution of nextFloat(Random) */
testNextFloat2()203     public void testNextFloat2() {
204         Random rnd = new Random();
205         rnd.setSeed(System.currentTimeMillis());
206         tstNextFloat(rnd);
207     }
208 
209     /**
210      * Generate 1000 values for nextFloat and check that
211      * p(value < 0.5) ~ 0.5. Use chi-square test
212      * with df = 2-1 = 1
213      * @param rnd Random to use if not null
214      */
tstNextFloat(Random rnd)215     private void tstNextFloat(Random rnd) {
216         int[] expected = new int[] {500,500};
217         int[] observed = new int[] {0,0};
218         float result = 0;
219         for (int i = 0; i < 1000; i ++) {
220             if (rnd == null) {
221                 result = RandomUtils.nextFloat();
222             } else {
223                 result = RandomUtils.nextFloat(rnd);
224             }
225             if (result < 0.5) {
226                 observed[0]++;
227             } else {
228                 observed[1]++;
229             }
230         }
231         /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
232          * Change to 6.64 for alpha = .01
233          */
234         assertTrue(
235             "chi-square test -- will fail about 1 in 1000 times",
236             chiSquare(expected,observed) < 10.83);
237     }
238 
239     /** test distribution of nextDouble() */
testNextDouble()240     public void testNextDouble() {
241         tstNextDouble(null);
242     }
243 
244     /** test distribution of nextDouble(Random) */
testNextDouble2()245     public void testNextDouble2() {
246         Random rnd = new Random();
247         rnd.setSeed(System.currentTimeMillis());
248         tstNextDouble(rnd);
249     }
250 
251     /**
252      * Generate 1000 values for nextFloat and check that
253      * p(value < 0.5) ~ 0.5. Use chi-square test
254      * with df = 2-1 = 1
255      * @param rnd Random to use if not null
256      */
tstNextDouble(Random rnd)257     private void tstNextDouble(Random rnd) {
258         int[] expected = new int[] {500,500};
259         int[] observed = new int[] {0,0};
260         double result = 0;
261         for (int i = 0; i < 1000; i ++) {
262             if (rnd == null) {
263                 result = RandomUtils.nextDouble();
264             } else {
265                 result = RandomUtils.nextDouble(rnd);
266             }
267             if (result < 0.5) {
268                 observed[0]++;
269             } else {
270                 observed[1]++;
271             }
272         }
273         /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
274          * Change to 6.64 for alpha = .01
275          */
276         assertTrue(
277             "chi-square test -- will fail about 1 in 1000 times",
278             chiSquare(expected,observed) < 10.83);
279     }
280 
281     /** make sure that unimplemented methods fail */
testUnimplementedMethods()282     public void testUnimplementedMethods() {
283 
284         try {
285             RandomUtils.JVM_RANDOM.setSeed(1000);
286             fail("expecting UnsupportedOperationException");
287         } catch (UnsupportedOperationException ex) {
288             // empty
289         }
290 
291         try {
292             RandomUtils.JVM_RANDOM.nextGaussian();
293             fail("expecting UnsupportedOperationException");
294         } catch (UnsupportedOperationException ex) {
295             // empty
296         }
297 
298         try {
299             RandomUtils.JVM_RANDOM.nextBytes(null);
300             fail("expecting UnsupportedOperationException");
301         } catch (UnsupportedOperationException ex) {
302             // empty
303         }
304 
305     }
306 
307     /** make sure that illegal arguments fail */
testIllegalArguments()308     public void testIllegalArguments() {
309 
310         try {
311             RandomUtils.JVM_RANDOM.nextInt(-1);
312             fail("expecting IllegalArgumentException");
313         } catch (IllegalArgumentException ex) {
314             // empty
315         }
316 
317         try {
318             JVMRandom.nextLong( -1L );
319             fail("expecting IllegalArgumentException");
320         } catch (IllegalArgumentException ex) {
321             // empty
322         }
323 
324     }
325 
326     /**
327      * Computes Chi-Square statistic given observed and expected counts
328      * @param observed array of observed frequency counts
329      * @param expected array of expected frequency counts
330      */
chiSquare(int[] expected, int[] observed)331     private double chiSquare(int[] expected, int[] observed) {
332         double sumSq = 0.0d;
333         double dev = 0.0d;
334         for (int i = 0; i< observed.length; i++) {
335             dev = (double)(observed[i] - expected[i]);
336             sumSq += dev*dev/(double)expected[i];
337         }
338         return sumSq;
339     }
340 
341 }
342 
343