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.lang.math;
018:
019: import java.util.Random;
020:
021: import junit.framework.Test;
022: import junit.framework.TestCase;
023: import junit.framework.TestSuite;
024:
025: /**
026: * Test cases for the {@link RandomUtils} class.
027: *
028: * @author <a href="mailto:phil@steitz.com">Phil Steitz</a>
029: * @version $Revision: 437554 $ $Date: 2006-08-27 23:21:41 -0700 (Sun, 27 Aug 2006) $
030: */
031:
032: public final class RandomUtilsTest extends TestCase {
033:
034: public RandomUtilsTest(String name) {
035: super (name);
036: }
037:
038: public void setUp() {
039: }
040:
041: public static Test suite() {
042: TestSuite suite = new TestSuite(RandomUtilsTest.class);
043: suite.setName("RandomUtils Tests");
044: return suite;
045: }
046:
047: /** test distribution of nextInt() */
048: public void testNextInt() {
049: tstNextInt(null);
050:
051: assertTrue(RandomUtils.nextInt() >= 0);
052: }
053:
054: /** test distribution of nextInt(Random) */
055: public void testNextInt2() {
056: Random rnd = new Random();
057: rnd.setSeed(System.currentTimeMillis());
058: tstNextInt(rnd);
059: }
060:
061: /** test distribution of JVMRandom.nextInt() */
062: public void testJvmRandomNextInt() {
063: tstNextInt(RandomUtils.JVM_RANDOM);
064: }
065:
066: /**
067: * Generate 1000 values for nextInt(bound) and compare
068: * the observed frequency counts to expected counts using
069: * a chi-square test.
070: * @param rnd Random to use if not null
071: */
072: private void tstNextInt(Random rnd) {
073: int bound = 0;
074: int result = 0;
075: // test boundary condition: n = Integer.MAX_VALUE;
076: bound = Integer.MAX_VALUE;
077: if (rnd == null) {
078: result = RandomUtils.nextInt(bound);
079: } else {
080: result = RandomUtils.nextInt(rnd, bound);
081: }
082: assertTrue("result less than bound", result < bound);
083: assertTrue("result non-negative", result >= 0);
084:
085: // test uniformity -- use Chi-Square test at .01 level
086: bound = 4;
087: int[] expected = new int[] { 250, 250, 250, 250 };
088: int[] observed = new int[] { 0, 0, 0, 0 };
089: for (int i = 0; i < 1000; i++) {
090: if (rnd == null) {
091: result = RandomUtils.nextInt(bound);
092: } else {
093: result = RandomUtils.nextInt(rnd, bound);
094: }
095: assertTrue(result < bound);
096: assertTrue(result >= 0);
097: observed[result]++;
098: }
099: /* Use ChiSquare dist with df = 4-1 = 3, alpha = .001
100: * Change to 11.34 for alpha = .01
101: */
102: assertTrue(
103: "chi-square test -- will fail about 1 in 1000 times",
104: chiSquare(expected, observed) < 16.27);
105: }
106:
107: /** test distribution of nextLong() */
108: public void testNextLong() {
109: tstNextLong(null);
110: }
111:
112: /** test distribution of nextLong(Random) BROKEN
113: * contract of nextLong(Random) is different from
114: * nextLong() */
115: public void testNextLong2() {
116: Random rnd = new Random();
117: rnd.setSeed(System.currentTimeMillis());
118: tstNextLong(rnd);
119: }
120:
121: /**
122: * Generate 1000 values for nextLong and check that
123: * p(value < long.MAXVALUE/2) ~ 0.5. Use chi-square test
124: * with df = 2-1 = 1
125: * @param rnd Random to use if not null
126: */
127: private void tstNextLong(Random rnd) {
128: int[] expected = new int[] { 500, 500 };
129: int[] observed = new int[] { 0, 0 };
130: long result = 0;
131: long midPoint = Long.MAX_VALUE / 2;
132: for (int i = 0; i < 1000; i++) {
133: if (rnd == null) {
134: result = Math.abs(RandomUtils.nextLong());
135: } else {
136: result = Math.abs(RandomUtils.nextLong(rnd));
137: }
138: if (result < midPoint) {
139: observed[0]++;
140: } else {
141: observed[1]++;
142: }
143: }
144: /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
145: * Change to 6.64 for alpha = .01
146: */
147: assertTrue(
148: "chi-square test -- will fail about 1 in 1000 times",
149: chiSquare(expected, observed) < 10.83);
150: }
151:
152: /** test distribution of nextBoolean() */
153: public void testNextBoolean() {
154: tstNextBoolean(null);
155: }
156:
157: /** test distribution of nextBoolean(Random) */
158: public void testNextBoolean2() {
159: Random rnd = new Random();
160: rnd.setSeed(System.currentTimeMillis());
161: tstNextBoolean(rnd);
162: }
163:
164: /**
165: * Generate 1000 values for nextBoolean and check that
166: * p(value = false) ~ 0.5. Use chi-square test
167: * with df = 2-1 = 1
168: * @param rnd Random to use if not null
169: */
170: private void tstNextBoolean(Random rnd) {
171: int[] expected = new int[] { 500, 500 };
172: int[] observed = new int[] { 0, 0 };
173: boolean result = false;
174: for (int i = 0; i < 1000; i++) {
175: if (rnd == null) {
176: result = RandomUtils.nextBoolean();
177: } else {
178: result = RandomUtils.nextBoolean(rnd);
179: }
180: if (result) {
181: observed[0]++;
182: } else {
183: observed[1]++;
184: }
185: }
186: /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
187: * Change to 6.64 for alpha = .01
188: */
189: assertTrue(
190: "chi-square test -- will fail about 1 in 1000 times",
191: chiSquare(expected, observed) < 10.83);
192: }
193:
194: /** test distribution of nextFloat() */
195: public void testNextFloat() {
196: tstNextFloat(null);
197: }
198:
199: /** test distribution of nextFloat(Random) */
200: public void testNextFloat2() {
201: Random rnd = new Random();
202: rnd.setSeed(System.currentTimeMillis());
203: tstNextFloat(rnd);
204: }
205:
206: /**
207: * Generate 1000 values for nextFloat and check that
208: * p(value < 0.5) ~ 0.5. Use chi-square test
209: * with df = 2-1 = 1
210: * @param rnd Random to use if not null
211: */
212: private void tstNextFloat(Random rnd) {
213: int[] expected = new int[] { 500, 500 };
214: int[] observed = new int[] { 0, 0 };
215: float result = 0;
216: for (int i = 0; i < 1000; i++) {
217: if (rnd == null) {
218: result = RandomUtils.nextFloat();
219: } else {
220: result = RandomUtils.nextFloat(rnd);
221: }
222: if (result < 0.5) {
223: observed[0]++;
224: } else {
225: observed[1]++;
226: }
227: }
228: /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
229: * Change to 6.64 for alpha = .01
230: */
231: assertTrue(
232: "chi-square test -- will fail about 1 in 1000 times",
233: chiSquare(expected, observed) < 10.83);
234: }
235:
236: /** test distribution of nextDouble() */
237: public void testNextDouble() {
238: tstNextDouble(null);
239: }
240:
241: /** test distribution of nextDouble(Random) */
242: public void testNextDouble2() {
243: Random rnd = new Random();
244: rnd.setSeed(System.currentTimeMillis());
245: tstNextDouble(rnd);
246: }
247:
248: /**
249: * Generate 1000 values for nextFloat and check that
250: * p(value < 0.5) ~ 0.5. Use chi-square test
251: * with df = 2-1 = 1
252: * @param rnd Random to use if not null
253: */
254: private void tstNextDouble(Random rnd) {
255: int[] expected = new int[] { 500, 500 };
256: int[] observed = new int[] { 0, 0 };
257: double result = 0;
258: for (int i = 0; i < 1000; i++) {
259: if (rnd == null) {
260: result = RandomUtils.nextDouble();
261: } else {
262: result = RandomUtils.nextDouble(rnd);
263: }
264: if (result < 0.5) {
265: observed[0]++;
266: } else {
267: observed[1]++;
268: }
269: }
270: /* Use ChiSquare dist with df = 2-1 = 1, alpha = .001
271: * Change to 6.64 for alpha = .01
272: */
273: assertTrue(
274: "chi-square test -- will fail about 1 in 1000 times",
275: chiSquare(expected, observed) < 10.83);
276: }
277:
278: /** make sure that unimplemented methods fail */
279: public void testUnimplementedMethods() {
280:
281: try {
282: RandomUtils.JVM_RANDOM.setSeed(1000);
283: fail("expecting UnsupportedOperationException");
284: } catch (UnsupportedOperationException ex) {
285: // empty
286: }
287:
288: try {
289: RandomUtils.JVM_RANDOM.nextGaussian();
290: fail("expecting UnsupportedOperationException");
291: } catch (UnsupportedOperationException ex) {
292: // empty
293: }
294:
295: try {
296: RandomUtils.JVM_RANDOM.nextBytes(null);
297: fail("expecting UnsupportedOperationException");
298: } catch (UnsupportedOperationException ex) {
299: // empty
300: }
301:
302: }
303:
304: /** make sure that illegal arguments fail */
305: public void testIllegalArguments() {
306:
307: try {
308: RandomUtils.JVM_RANDOM.nextInt(-1);
309: fail("expecting IllegalArgumentException");
310: } catch (IllegalArgumentException ex) {
311: // empty
312: }
313:
314: try {
315: JVMRandom.nextLong(-1L);
316: fail("expecting IllegalArgumentException");
317: } catch (IllegalArgumentException ex) {
318: // empty
319: }
320:
321: }
322:
323: /**
324: * Computes Chi-Square statistic given observed and expected counts
325: * @param observed array of observed frequency counts
326: * @param expected array of expected frequency counts
327: */
328: private double chiSquare(int[] expected, int[] observed) {
329: double sumSq = 0.0d;
330: double dev = 0.0d;
331: for (int i = 0; i < observed.length; i++) {
332: dev = (double) (observed[i] - expected[i]);
333: sumSq += dev * dev / (double) expected[i];
334: }
335: return sumSq;
336: }
337:
338: }
|