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.math.random; 018 019 import junit.framework.Test; 020 import junit.framework.TestSuite; 021 import java.util.HashSet; 022 023 import org.apache.commons.math.RetryTestCase; 024 import org.apache.commons.math.stat.Frequency; 025 import org.apache.commons.math.stat.inference.ChiSquareTestImpl; 026 import org.apache.commons.math.stat.descriptive.SummaryStatistics; 027 028 /** 029 * Test cases for the RandomData class. 030 * 031 * @version $Revision: 783041 $ $Date: 2009-04-05 11:55:59 -0500 (Sun, 05 Apr 032 * 2009) $ 033 */ 034 035 public class RandomDataTest extends RetryTestCase { 036 037 public RandomDataTest(String name) { 038 super(name); 039 randomData = new RandomDataImpl(); 040 } 041 042 protected long smallSampleSize = 1000; 043 protected double[] expected = { 250, 250, 250, 250 }; 044 protected int largeSampleSize = 10000; 045 private String[] hex = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 046 "a", "b", "c", "d", "e", "f" }; 047 protected RandomDataImpl randomData = null; 048 protected ChiSquareTestImpl testStatistic = new ChiSquareTestImpl(); 049 050 public static Test suite() { 051 TestSuite suite = new TestSuite(RandomDataTest.class); 052 suite.setName("RandomData Tests"); 053 return suite; 054 } 055 056 public void testNextIntExtremeValues() { 057 int x = randomData.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE); 058 int y = randomData.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE); 059 assertFalse(x == y); 060 } 061 062 public void testNextLongExtremeValues() { 063 long x = randomData.nextLong(Long.MIN_VALUE, Long.MAX_VALUE); 064 long y = randomData.nextLong(Long.MIN_VALUE, Long.MAX_VALUE); 065 assertFalse(x == y); 066 } 067 068 /** test dispersion and failure modes for nextInt() */ 069 public void testNextInt() { 070 try { 071 randomData.nextInt(4, 3); 072 fail("IllegalArgumentException expected"); 073 } catch (IllegalArgumentException ex) { 074 // ignored 075 } 076 Frequency freq = new Frequency(); 077 int value = 0; 078 for (int i = 0; i < smallSampleSize; i++) { 079 value = randomData.nextInt(0, 3); 080 assertTrue("nextInt range", (value >= 0) && (value <= 3)); 081 freq.addValue(value); 082 } 083 long[] observed = new long[4]; 084 for (int i = 0; i < 4; i++) { 085 observed[i] = freq.getCount(i); 086 } 087 088 /* 089 * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34 090 * for alpha = .01 091 */ 092 assertTrue("chi-square test -- will fail about 1 in 1000 times", 093 testStatistic.chiSquare(expected, observed) < 16.27); 094 } 095 096 /** test dispersion and failure modes for nextLong() */ 097 public void testNextLong() { 098 try { 099 randomData.nextLong(4, 3); 100 fail("IllegalArgumentException expected"); 101 } catch (IllegalArgumentException ex) { 102 // ignored 103 } 104 Frequency freq = new Frequency(); 105 long value = 0; 106 for (int i = 0; i < smallSampleSize; i++) { 107 value = randomData.nextLong(0, 3); 108 assertTrue("nextInt range", (value >= 0) && (value <= 3)); 109 freq.addValue(value); 110 } 111 long[] observed = new long[4]; 112 for (int i = 0; i < 4; i++) { 113 observed[i] = freq.getCount(i); 114 } 115 116 /* 117 * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34 118 * for alpha = .01 119 */ 120 assertTrue("chi-square test -- will fail about 1 in 1000 times", 121 testStatistic.chiSquare(expected, observed) < 16.27); 122 } 123 124 /** test dispersion and failure modes for nextSecureLong() */ 125 public void testNextSecureLong() { 126 try { 127 randomData.nextSecureLong(4, 3); 128 fail("IllegalArgumentException expected"); 129 } catch (IllegalArgumentException ex) { 130 // ignored 131 } 132 Frequency freq = new Frequency(); 133 long value = 0; 134 for (int i = 0; i < smallSampleSize; i++) { 135 value = randomData.nextSecureLong(0, 3); 136 assertTrue("nextInt range", (value >= 0) && (value <= 3)); 137 freq.addValue(value); 138 } 139 long[] observed = new long[4]; 140 for (int i = 0; i < 4; i++) { 141 observed[i] = freq.getCount(i); 142 } 143 144 /* 145 * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34 146 * for alpha = .01 147 */ 148 assertTrue("chi-square test -- will fail about 1 in 1000 times", 149 testStatistic.chiSquare(expected, observed) < 16.27); 150 } 151 152 /** test dispersion and failure modes for nextSecureInt() */ 153 public void testNextSecureInt() { 154 try { 155 randomData.nextSecureInt(4, 3); 156 fail("IllegalArgumentException expected"); 157 } catch (IllegalArgumentException ex) { 158 // ignored 159 } 160 Frequency freq = new Frequency(); 161 int value = 0; 162 for (int i = 0; i < smallSampleSize; i++) { 163 value = randomData.nextSecureInt(0, 3); 164 assertTrue("nextInt range", (value >= 0) && (value <= 3)); 165 freq.addValue(value); 166 } 167 long[] observed = new long[4]; 168 for (int i = 0; i < 4; i++) { 169 observed[i] = freq.getCount(i); 170 } 171 172 /* 173 * Use ChiSquare dist with df = 4-1 = 3, alpha = .001 Change to 11.34 174 * for alpha = .01 175 */ 176 assertTrue("chi-square test -- will fail about 1 in 1000 times", 177 testStatistic.chiSquare(expected, observed) < 16.27); 178 } 179 180 /** 181 * Make sure that empirical distribution of random Poisson(4)'s has P(X <= 182 * 5) close to actual cumulative Poisson probablity and that nextPoisson 183 * fails when mean is non-positive TODO: replace with statistical test, 184 * adding test stat to TestStatistic 185 */ 186 public void testNextPoisson() { 187 try { 188 randomData.nextPoisson(0); 189 fail("zero mean -- expecting IllegalArgumentException"); 190 } catch (IllegalArgumentException ex) { 191 // ignored 192 } 193 Frequency f = new Frequency(); 194 for (int i = 0; i < largeSampleSize; i++) { 195 try { 196 f.addValue(randomData.nextPoisson(4.0d)); 197 } catch (Exception ex) { 198 fail(ex.getMessage()); 199 } 200 } 201 long cumFreq = f.getCount(0) + f.getCount(1) + f.getCount(2) 202 + f.getCount(3) + f.getCount(4) + f.getCount(5); 203 long sumFreq = f.getSumFreq(); 204 double cumPct = Double.valueOf(cumFreq).doubleValue() 205 / Double.valueOf(sumFreq).doubleValue(); 206 assertEquals("cum Poisson(4)", cumPct, 0.7851, 0.2); 207 try { 208 randomData.nextPoisson(-1); 209 fail("negative mean supplied -- IllegalArgumentException expected"); 210 } catch (IllegalArgumentException ex) { 211 // ignored 212 } 213 try { 214 randomData.nextPoisson(0); 215 fail("0 mean supplied -- IllegalArgumentException expected"); 216 } catch (IllegalArgumentException ex) { 217 // ignored 218 } 219 220 } 221 222 public void testNextPoissonLargeMean() { 223 for (int i = 0; i < 1000; i++) { 224 long n = randomData.nextPoisson(1500.0); 225 assertTrue(0 <= n); 226 } 227 } 228 229 /** test dispersion and failute modes for nextHex() */ 230 public void testNextHex() { 231 try { 232 randomData.nextHexString(-1); 233 fail("negative length supplied -- IllegalArgumentException expected"); 234 } catch (IllegalArgumentException ex) { 235 // ignored 236 } 237 try { 238 randomData.nextHexString(0); 239 fail("zero length supplied -- IllegalArgumentException expected"); 240 } catch (IllegalArgumentException ex) { 241 // ignored 242 } 243 String hexString = randomData.nextHexString(3); 244 if (hexString.length() != 3) { 245 fail("incorrect length for generated string"); 246 } 247 hexString = randomData.nextHexString(1); 248 if (hexString.length() != 1) { 249 fail("incorrect length for generated string"); 250 } 251 try { 252 hexString = randomData.nextHexString(0); 253 fail("zero length requested -- expecting IllegalArgumentException"); 254 } catch (IllegalArgumentException ex) { 255 // ignored 256 } 257 if (hexString.length() != 1) { 258 fail("incorrect length for generated string"); 259 } 260 Frequency f = new Frequency(); 261 for (int i = 0; i < smallSampleSize; i++) { 262 hexString = randomData.nextHexString(100); 263 if (hexString.length() != 100) { 264 fail("incorrect length for generated string"); 265 } 266 for (int j = 0; j < hexString.length(); j++) { 267 f.addValue(hexString.substring(j, j + 1)); 268 } 269 } 270 double[] expected = new double[16]; 271 long[] observed = new long[16]; 272 for (int i = 0; i < 16; i++) { 273 expected[i] = (double) smallSampleSize * 100 / 16; 274 observed[i] = f.getCount(hex[i]); 275 } 276 /* 277 * Use ChiSquare dist with df = 16-1 = 15, alpha = .001 Change to 30.58 278 * for alpha = .01 279 */ 280 assertTrue("chi-square test -- will fail about 1 in 1000 times", 281 testStatistic.chiSquare(expected, observed) < 37.70); 282 } 283 284 /** test dispersion and failute modes for nextHex() */ 285 public void testNextSecureHex() { 286 try { 287 randomData.nextSecureHexString(-1); 288 fail("negative length -- IllegalArgumentException expected"); 289 } catch (IllegalArgumentException ex) { 290 // ignored 291 } 292 try { 293 randomData.nextSecureHexString(0); 294 fail("zero length -- IllegalArgumentException expected"); 295 } catch (IllegalArgumentException ex) { 296 // ignored 297 } 298 String hexString = randomData.nextSecureHexString(3); 299 if (hexString.length() != 3) { 300 fail("incorrect length for generated string"); 301 } 302 hexString = randomData.nextSecureHexString(1); 303 if (hexString.length() != 1) { 304 fail("incorrect length for generated string"); 305 } 306 try { 307 hexString = randomData.nextSecureHexString(0); 308 fail("zero length requested -- expecting IllegalArgumentException"); 309 } catch (IllegalArgumentException ex) { 310 // ignored 311 } 312 if (hexString.length() != 1) { 313 fail("incorrect length for generated string"); 314 } 315 Frequency f = new Frequency(); 316 for (int i = 0; i < smallSampleSize; i++) { 317 hexString = randomData.nextSecureHexString(100); 318 if (hexString.length() != 100) { 319 fail("incorrect length for generated string"); 320 } 321 for (int j = 0; j < hexString.length(); j++) { 322 f.addValue(hexString.substring(j, j + 1)); 323 } 324 } 325 double[] expected = new double[16]; 326 long[] observed = new long[16]; 327 for (int i = 0; i < 16; i++) { 328 expected[i] = (double) smallSampleSize * 100 / 16; 329 observed[i] = f.getCount(hex[i]); 330 } 331 /* 332 * Use ChiSquare dist with df = 16-1 = 15, alpha = .001 Change to 30.58 333 * for alpha = .01 334 */ 335 assertTrue("chi-square test -- will fail about 1 in 1000 times", 336 testStatistic.chiSquare(expected, observed) < 37.70); 337 } 338 339 /** test failure modes and dispersion of nextUniform() */ 340 public void testNextUniform() { 341 try { 342 randomData.nextUniform(4, 3); 343 fail("IllegalArgumentException expected"); 344 } catch (IllegalArgumentException ex) { 345 // ignored 346 } 347 try { 348 randomData.nextUniform(3, 3); 349 fail("IllegalArgumentException expected"); 350 } catch (IllegalArgumentException ex) { 351 // ignored 352 } 353 double[] expected = { 500, 500 }; 354 long[] observed = { 0, 0 }; 355 double lower = -1d; 356 double upper = 20d; 357 double midpoint = (lower + upper) / 2d; 358 double result = 0; 359 for (int i = 0; i < 1000; i++) { 360 result = randomData.nextUniform(lower, upper); 361 if ((result == lower) || (result == upper)) { 362 fail("generated value equal to an endpoint: " + result); 363 } 364 if (result < midpoint) { 365 observed[0]++; 366 } else { 367 observed[1]++; 368 } 369 } 370 /* 371 * Use ChiSquare dist with df = 2-1 = 1, alpha = .001 Change to 6.64 for 372 * alpha = .01 373 */ 374 assertTrue("chi-square test -- will fail about 1 in 1000 times", 375 testStatistic.chiSquare(expected, observed) < 10.83); 376 } 377 378 /** test exclusive endpoints of nextUniform **/ 379 public void testNextUniformExclusiveEndpoints() { 380 for (int i = 0; i < 1000; i++) { 381 double u = randomData.nextUniform(0.99, 1); 382 assertTrue(u > 0.99 && u < 1); 383 } 384 } 385 386 /** test failure modes and distribution of nextGaussian() */ 387 public void testNextGaussian() { 388 try { 389 randomData.nextGaussian(0, 0); 390 fail("zero sigma -- IllegalArgumentException expected"); 391 } catch (IllegalArgumentException ex) { 392 // ignored 393 } 394 SummaryStatistics u = new SummaryStatistics(); 395 for (int i = 0; i < largeSampleSize; i++) { 396 u.addValue(randomData.nextGaussian(0, 1)); 397 } 398 double xbar = u.getMean(); 399 double s = u.getStandardDeviation(); 400 double n = u.getN(); 401 /* 402 * t-test at .001-level TODO: replace with externalized t-test, with 403 * test statistic defined in TestStatistic 404 */ 405 assertTrue(Math.abs(xbar) / (s / Math.sqrt(n)) < 3.29); 406 } 407 408 /** test failure modes and distribution of nextExponential() */ 409 public void testNextExponential() { 410 try { 411 randomData.nextExponential(-1); 412 fail("negative mean -- expecting IllegalArgumentException"); 413 } catch (IllegalArgumentException ex) { 414 // ignored 415 } 416 assertEquals("0 mean", 0, randomData.nextExponential(0), 10E-8); 417 long cumFreq = 0; 418 double v = 0; 419 for (int i = 0; i < largeSampleSize; i++) { 420 v = randomData.nextExponential(1); 421 assertTrue("exponential deviate postive", v > 0); 422 if (v < 2) 423 cumFreq++; 424 } 425 /* 426 * TODO: Replace with a statistical test, with statistic added to 427 * TestStatistic. Check below compares observed cumulative distribution 428 * evaluated at 2 with exponential CDF 429 */ 430 assertEquals("exponential cumulative distribution", (double) cumFreq 431 / (double) largeSampleSize, 0.8646647167633873, .2); 432 } 433 434 /** test reseeding, algorithm/provider games */ 435 public void testConfig() { 436 randomData.reSeed(1000); 437 double v = randomData.nextUniform(0, 1); 438 randomData.reSeed(); 439 assertTrue("different seeds", Math 440 .abs(v - randomData.nextUniform(0, 1)) > 10E-12); 441 randomData.reSeed(1000); 442 assertEquals("same seeds", v, randomData.nextUniform(0, 1), 10E-12); 443 randomData.reSeedSecure(1000); 444 String hex = randomData.nextSecureHexString(40); 445 randomData.reSeedSecure(); 446 assertTrue("different seeds", !hex.equals(randomData 447 .nextSecureHexString(40))); 448 randomData.reSeedSecure(1000); 449 assertTrue("same seeds", !hex 450 .equals(randomData.nextSecureHexString(40))); 451 452 /* 453 * remove this test back soon, since it takes about 4 seconds 454 * 455 * try { randomData.setSecureAlgorithm("SHA1PRNG","SUN"); } catch 456 * (NoSuchProviderException ex) { ; } assertTrue("different seeds", 457 * !hex.equals(randomData.nextSecureHexString(40))); try { 458 * randomData.setSecureAlgorithm("NOSUCHTHING","SUN"); 459 * fail("expecting NoSuchAlgorithmException"); } catch 460 * (NoSuchProviderException ex) { ; } catch (NoSuchAlgorithmException 461 * ex) { ; } 462 * 463 * try { randomData.setSecureAlgorithm("SHA1PRNG","NOSUCHPROVIDER"); 464 * fail("expecting NoSuchProviderException"); } catch 465 * (NoSuchProviderException ex) { ; } 466 */ 467 468 // test reseeding without first using the generators 469 RandomDataImpl rd = new RandomDataImpl(); 470 rd.reSeed(100); 471 rd.nextLong(1, 2); 472 RandomDataImpl rd2 = new RandomDataImpl(); 473 rd2.reSeedSecure(2000); 474 rd2.nextSecureLong(1, 2); 475 rd = new RandomDataImpl(); 476 rd.reSeed(); 477 rd.nextLong(1, 2); 478 rd2 = new RandomDataImpl(); 479 rd2.reSeedSecure(); 480 rd2.nextSecureLong(1, 2); 481 } 482 483 /** tests for nextSample() sampling from Collection */ 484 public void testNextSample() { 485 Object[][] c = { { "0", "1" }, { "0", "2" }, { "0", "3" }, 486 { "0", "4" }, { "1", "2" }, { "1", "3" }, { "1", "4" }, 487 { "2", "3" }, { "2", "4" }, { "3", "4" } }; 488 long[] observed = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 489 double[] expected = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }; 490 491 HashSet<Object> cPop = new HashSet<Object>(); // {0,1,2,3,4} 492 for (int i = 0; i < 5; i++) { 493 cPop.add(Integer.toString(i)); 494 } 495 496 Object[] sets = new Object[10]; // 2-sets from 5 497 for (int i = 0; i < 10; i++) { 498 HashSet<Object> hs = new HashSet<Object>(); 499 hs.add(c[i][0]); 500 hs.add(c[i][1]); 501 sets[i] = hs; 502 } 503 504 for (int i = 0; i < 1000; i++) { 505 Object[] cSamp = randomData.nextSample(cPop, 2); 506 observed[findSample(sets, cSamp)]++; 507 } 508 509 /* 510 * Use ChiSquare dist with df = 10-1 = 9, alpha = .001 Change to 21.67 511 * for alpha = .01 512 */ 513 assertTrue("chi-square test -- will fail about 1 in 1000 times", 514 testStatistic.chiSquare(expected, observed) < 27.88); 515 516 // Make sure sample of size = size of collection returns same collection 517 HashSet<Object> hs = new HashSet<Object>(); 518 hs.add("one"); 519 Object[] one = randomData.nextSample(hs, 1); 520 String oneString = (String) one[0]; 521 if ((one.length != 1) || !oneString.equals("one")) { 522 fail("bad sample for set size = 1, sample size = 1"); 523 } 524 525 // Make sure we fail for sample size > collection size 526 try { 527 one = randomData.nextSample(hs, 2); 528 fail("sample size > set size, expecting IllegalArgumentException"); 529 } catch (IllegalArgumentException ex) { 530 // ignored 531 } 532 533 // Make sure we fail for empty collection 534 try { 535 hs = new HashSet<Object>(); 536 one = randomData.nextSample(hs, 0); 537 fail("n = k = 0, expecting IllegalArgumentException"); 538 } catch (IllegalArgumentException ex) { 539 // ignored 540 } 541 } 542 543 @SuppressWarnings("unchecked") 544 private int findSample(Object[] u, Object[] samp) { 545 for (int i = 0; i < u.length; i++) { 546 HashSet<Object> set = (HashSet<Object>) u[i]; 547 HashSet<Object> sampSet = new HashSet<Object>(); 548 for (int j = 0; j < samp.length; j++) { 549 sampSet.add(samp[j]); 550 } 551 if (set.equals(sampSet)) { 552 return i; 553 } 554 } 555 fail("sample not found:{" + samp[0] + "," + samp[1] + "}"); 556 return -1; 557 } 558 559 /** tests for nextPermutation */ 560 public void testNextPermutation() { 561 int[][] p = { { 0, 1, 2 }, { 0, 2, 1 }, { 1, 0, 2 }, { 1, 2, 0 }, 562 { 2, 0, 1 }, { 2, 1, 0 } }; 563 long[] observed = { 0, 0, 0, 0, 0, 0 }; 564 double[] expected = { 100, 100, 100, 100, 100, 100 }; 565 566 for (int i = 0; i < 600; i++) { 567 int[] perm = randomData.nextPermutation(3, 3); 568 observed[findPerm(p, perm)]++; 569 } 570 571 /* 572 * Use ChiSquare dist with df = 6-1 = 5, alpha = .001 Change to 15.09 573 * for alpha = .01 574 */ 575 assertTrue("chi-square test -- will fail about 1 in 1000 times", 576 testStatistic.chiSquare(expected, observed) < 20.52); 577 578 // Check size = 1 boundary case 579 int[] perm = randomData.nextPermutation(1, 1); 580 if ((perm.length != 1) || (perm[0] != 0)) { 581 fail("bad permutation for n = 1, sample k = 1"); 582 583 // Make sure we fail for k size > n 584 try { 585 perm = randomData.nextPermutation(2, 3); 586 fail("permutation k > n, expecting IllegalArgumentException"); 587 } catch (IllegalArgumentException ex) { 588 // ignored 589 } 590 591 // Make sure we fail for n = 0 592 try { 593 perm = randomData.nextPermutation(0, 0); 594 fail("permutation k = n = 0, expecting IllegalArgumentException"); 595 } catch (IllegalArgumentException ex) { 596 // ignored 597 } 598 599 // Make sure we fail for k < n < 0 600 try { 601 perm = randomData.nextPermutation(-1, -3); 602 fail("permutation k < n < 0, expecting IllegalArgumentException"); 603 } catch (IllegalArgumentException ex) { 604 // ignored 605 } 606 607 } 608 } 609 610 // Disable until we have equals 611 //public void testSerial() { 612 // assertEquals(randomData, TestUtils.serializeAndRecover(randomData)); 613 //} 614 615 private int findPerm(int[][] p, int[] samp) { 616 for (int i = 0; i < p.length; i++) { 617 boolean good = true; 618 for (int j = 0; j < samp.length; j++) { 619 if (samp[j] != p[i][j]) { 620 good = false; 621 } 622 } 623 if (good) { 624 return i; 625 } 626 } 627 fail("permutation not found"); 628 return -1; 629 } 630 }