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