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