undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,87 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/*
* @test
* @bug 4949279 6937857
* @summary Independent instantiations of Random() have distinct seeds.
* @key randomness
*/
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
public class DistinctSeeds {
public static void main(String[] args) throws Exception {
// Strictly speaking, it is possible for these to randomly fail,
// but the probability should be small (approximately 2**-48).
if (new Random().nextLong() == new Random().nextLong() ||
new Random().nextLong() == new Random().nextLong())
throw new RuntimeException("Random() seeds not unique.");
// Now try generating seeds concurrently
class RandomCollector implements Runnable {
long[] randoms = new long[1<<17];
public void run() {
for (int i = 0; i < randoms.length; i++)
randoms[i] = new Random().nextLong();
}
}
final int threadCount = 2;
List<RandomCollector> collectors = new ArrayList<>();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
RandomCollector r = new RandomCollector();
collectors.add(r);
threads.add(new Thread(r));
}
for (Thread thread : threads)
thread.start();
for (Thread thread : threads)
thread.join();
int collisions = 0;
HashSet<Long> s = new HashSet<>();
for (RandomCollector r : collectors) {
for (long x : r.randoms) {
if (s.contains(x))
collisions++;
s.add(x);
}
}
System.out.printf("collisions=%d%n", collisions);
if (collisions > 10)
throw new Error("too many collisions");
}
}

View file

@ -0,0 +1,349 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import jdk.test.lib.RandomFactory;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.random.RandomGenerator;
import java.util.stream.DoubleStream;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @test
* @bug 8302987
* @key randomness
*
* @summary Check consistency of RandomGenerator::equiDoubles
* @library /test/lib
* @run junit EquiDoublesTest
*
*/
public class EquiDoublesTest {
private static final int SAMPLES = 100_000;
/*
* A factor to use in the tight*() tests to make sure that
* all equidistant doubles are generated.
*/
private static final long SAFETY_FACTOR = 100L;
private static final RandomGenerator rnd = RandomFactory.getRandom();
private static double nextUp(double d, int steps) {
for (int i = 0; i < steps; ++i) {
d = Math.nextUp(d);
}
return d;
}
private static double nextDown(double d, int steps) {
for (int i = 0; i < steps; ++i) {
d = Math.nextDown(d);
}
return d;
}
static Arguments[] equi() {
return new Arguments[] {
arguments(0.0, 1e-9),
arguments(1.0, 1.1),
arguments(1.0e23, 1.1e23),
arguments(1.0e300, 1.1e300),
arguments(-1.2, 1.1),
arguments(-1.2e-30, 1.1e6),
arguments(-Double.MIN_VALUE, Double.MIN_VALUE),
arguments(-Double.MAX_VALUE, Double.MAX_VALUE),
};
}
@ParameterizedTest
@MethodSource
void equi(double l, double r) {
double[] minmax = new double[2];
resetMinmax(minmax);
DoubleStream equi = rnd.equiDoubles(l, r, true, false);
equi.limit(SAMPLES).forEach(d -> updateMinmax(minmax, d));
assertTrue(l <= minmax[0]);
assertTrue(minmax[1] < r);
resetMinmax(minmax);
equi = rnd.equiDoubles(l, r, true, true);
equi.limit(SAMPLES).forEach(d -> updateMinmax(minmax, d));
assertTrue(l <= minmax[0]);
assertTrue(minmax[1] <= r);
resetMinmax(minmax);
equi = rnd.equiDoubles(l, r, false, true);
equi.limit(SAMPLES).forEach(d -> updateMinmax(minmax, d));
assertTrue(l < minmax[0]);
assertTrue(minmax[1] <= r);
resetMinmax(minmax);
equi = rnd.equiDoubles(l, r, false, false);
equi.limit(SAMPLES).forEach(d -> updateMinmax(minmax, d));
assertTrue(l < minmax[0]);
assertTrue(minmax[1] < r);
/* with negated intervals */
resetMinmax(minmax);
equi = rnd.equiDoubles(-r, -l, true, false);
equi.limit(SAMPLES).forEach(d -> updateMinmax(minmax, d));
assertTrue(-r <= minmax[0]);
assertTrue(minmax[1] < -l);
resetMinmax(minmax);
equi = rnd.equiDoubles(-r, -l, true, true);
equi.limit(SAMPLES).forEach(d -> updateMinmax(minmax, d));
assertTrue(-r <= minmax[0]);
assertTrue(minmax[1] <= -l);
resetMinmax(minmax);
equi = rnd.equiDoubles(-r, -l, false, true);
equi.limit(SAMPLES).forEach(d -> updateMinmax(minmax, d));
assertTrue(-r < minmax[0]);
assertTrue(minmax[1] <= -l);
resetMinmax(minmax);
equi = rnd.equiDoubles(-r, -l, false, false);
equi.limit(SAMPLES).forEach(d -> updateMinmax(minmax, d));
assertTrue(-r < minmax[0]);
assertTrue(minmax[1] < -l);
}
private void resetMinmax(double[] minmax) {
minmax[0] = Double.POSITIVE_INFINITY;
minmax[1] = Double.NEGATIVE_INFINITY;
}
private void updateMinmax(double[] minmax, double d) {
if (d < minmax[0]) {
minmax[0] = d;
}
if (d > minmax[1]) {
minmax[1] = d;
}
}
static Arguments[] tight() {
return new Arguments[] {
arguments(0.0, (short) 100),
arguments(1.0, (short) 100),
arguments(1.1, (short) 100),
arguments(1.0e23, (short) 100),
arguments(1.0e300, (short) 100),
arguments(-1.2, (short) 100),
arguments(-1.2e-30, (short) 100),
arguments(-Double.MIN_VALUE, (short) 100),
arguments(-Double.MIN_VALUE, (short) 2),
arguments(-Double.MAX_VALUE, (short) 2),
};
}
/*
* All equidistant doubles in a tight range are expected to be generated.
* The arguments must be chosen as to not overlap a value with irregular
* spacing around it.
*/
@ParameterizedTest
@MethodSource
void tight(double l, short steps) {
double r = nextUp(l, steps);
TreeSet<Double> set = new TreeSet<>();
DoubleStream equi = rnd.equiDoubles(l, r, true, false);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(l, r, true, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps + 1, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(l, r, false, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(l, r, false, false);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps - 1, set.size());
checkEquidistance(set);
/* with negated intervals */
set.clear();
equi = rnd.equiDoubles(-r, -l, true, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps + 1, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(-r, -l, true, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps + 1, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(-r, -l, false, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(-r, -l, false, false);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps - 1, set.size());
checkEquidistance(set);
}
static Arguments[] tightWithIrregularSpacing() {
return new Arguments[] {
arguments(0x1p-1, (short) 15, (short) 23),
arguments(0x1p0, (short) 17, (short) 5),
arguments(0x1p1, (short) 7, (short) 8),
arguments(0x1p-600, (short) 28, (short) 33),
arguments(0x1p600, (short) 9, (short) 19),
};
}
/*
* m must be a power of 2 greater than Double.MIN_NORMAL
*/
@ParameterizedTest
@MethodSource
void tightWithIrregularSpacing(double m, short lSteps, short rSteps) {
double l = nextDown(m, 2 * lSteps);
double r = nextUp(m, rSteps);
int steps = lSteps + rSteps;
TreeSet<Double> set = new TreeSet<>();
DoubleStream equi = rnd.equiDoubles(l, r, true, false);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(l, r, true, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps + 1, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(l, r, false, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(l, r, false, false);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps - 1, set.size());
checkEquidistance(set);
/* with negated intervals */
set.clear();
equi = rnd.equiDoubles(-r, -l, true, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps + 1, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(-r, -l, true, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps + 1, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(-r, -l, false, true);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps, set.size());
checkEquidistance(set);
set.clear();
equi = rnd.equiDoubles(-r, -l, false, false);
equi.limit(SAFETY_FACTOR * steps).forEach(set::add);
assertEquals(steps - 1, set.size());
checkEquidistance(set);
}
private void checkEquidistance(TreeSet<Double> set) {
if (set.size() < 3) {
return;
}
Iterator<Double> iter = set.iterator();
double prev = iter.next();
double curr = iter.next();
double delta = curr - prev;
while (iter.hasNext()) {
prev = curr;
curr = iter.next();
assertEquals(delta, curr - prev);
}
}
static Arguments[] empty() {
return new Arguments[] {
arguments(1.0),
arguments(-1.0),
arguments(0.0),
arguments(nextDown(Double.MAX_VALUE, 1)),
arguments(nextUp(-Double.MAX_VALUE, 1)),
};
}
@ParameterizedTest
@MethodSource
void empty(double l) {
assertThrows(IllegalArgumentException.class,
() -> rnd.equiDoubles(l, l, true, false)
);
assertThrows(IllegalArgumentException.class,
() -> rnd.equiDoubles(l, nextUp(l, 1), false, false)
);
assertThrows(IllegalArgumentException.class,
() -> rnd.equiDoubles(nextDown(l, 1), l, false, false)
);
assertThrows(IllegalArgumentException.class,
() -> rnd.equiDoubles(l, l, false, true)
);
assertThrows(IllegalArgumentException.class,
() -> rnd.equiDoubles(l, nextDown(l, 1), true, true)
);
assertThrows(IllegalArgumentException.class,
() -> rnd.equiDoubles(nextUp(l, 1), l, true, true)
);
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.random.RandomGeneratorFactory;
/**
* @test
* @summary Check that the (byte[]) constructors do not throw (see bug report)
* @bug 8283083
*/
public class LXMRandomWithSeed {
public static void main(String[] args) {
byte[] seed = new byte[0x100];
for (var i = 0; i < seed.length; ++i) {
seed[i] = (byte) i;
}
var lxmFactories = RandomGeneratorFactory.all()
.filter(factory -> factory.group().equals("LXM"))
.toList();
for (var lxmFactory : lxmFactories) {
var lxmGen0 = lxmFactory.create(seed);
var lxmGen1 = lxmFactory.create(seed);
if (lxmGen0.nextLong() != lxmGen1.nextLong()) {
throw new RuntimeException("%s(byte[]) is incorrect".formatted(lxmFactory.name()));
}
}
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4261170
* @summary Tests for Random.nextBytes
* @author Martin Buchholz
*/
import java.util.Arrays;
import java.util.Random;
public class NextBytes {
private static void realMain(String[] args) throws Throwable {
byte[] expected = new byte[]
{27, -105, -24, 83, -77, -29, 119, -74, -106, 68, 54};
Random r = new java.util.Random(2398579034L);
for (int i = 0; i <= expected.length; i++) {
r.setSeed(2398579034L);
byte[] actual = new byte[i];
r.nextBytes(actual);
//System.out.println(Arrays.toString(actual));
check(Arrays.equals(actual, Arrays.copyOf(expected,i)));
}
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4187412
* @summary The FCS release of 1.2 did not special case the Random.nextInt
* calculation for a power of two modulus, as mandated by the spec.
*/
import java.util.Random;
public class NextIntPowerOfTwoMod {
public static void main(String[] args) throws Exception {
Random r = new Random(69);
int total = 0;
for (int i=0; i<1000; i++)
total += r.nextInt(16);
if (total != 7639)
throw new RuntimeException("Not using correct algorithm.");
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Comparator;
import java.util.random.RandomGenerator;
import java.util.random.RandomGenerator.*;
import java.util.random.RandomGeneratorFactory;
/**
* @test
* @summary test bit sequences produced by clases that implement interface RandomGenerator
* @bug 8248862
* @run main RandomCanaryPi
* @key randomness
*/
public class RandomCanaryPi {
static double pi(RandomGenerator rng) {
int N = 10000000;
int k = 0;
for (int i = 0; i < N; i++) {
double x = rng.nextDouble();
double y = rng.nextDouble();
if (x * x + y * y <= 1.0) {
k++;
}
}
return 4.0 * (double)k / (double)N;
}
static int failed = 0;
public static void main(String[] args) {
RandomGeneratorFactory.all()
.sorted(Comparator.comparing(RandomGeneratorFactory::name))
.forEach(factory -> {
RandomGenerator rng = factory.create();
double pi = pi(rng);
double delta = Math.abs(Math.PI - pi);
boolean pass = delta < 1E-2;
if (!pass) {
System.err.println("Algorithm = " + factory.name() + " failed");
System.err.println("Actual = " + Math.PI);
System.err.println("Monte Carlo = " + pi);
System.err.println("Delta = " + delta);
System.err.println();
failed++;
}
});
if (failed != 0) {
throw new RuntimeException(failed + " tests failed");
}
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import jdk.test.lib.RandomFactory;
/**
* @test
* @summary Check that nextExponential() returns non-negative outcomes
* @bug 8284866
*
* @key randomness
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main RandomExponentialTest
*/
public class RandomExponentialTest {
private static final int SAMPLES = 1_000_000_000;
public static void main(String[] args) throws Exception {
var errCount = 0;
var errSample = Double.NaN;
var random = RandomFactory.getRandom();
for (int i = 0; i < SAMPLES; i++) {
var expVal = random.nextExponential();
if (!(expVal >= 0.0)) {
errCount += 1;
errSample = expVal;
}
}
if (errCount > 0) {
throw new RuntimeException("%d errors out of %d samples: e.g., %f"
.formatted(errCount, SAMPLES, errSample));
}
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @summary Verify nextDouble stays within range
* @bug 8280550 8280950 8281183 8202449
*
* @key randomness
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main RandomNextDoubleBoundary
*/
import java.util.SplittableRandom;
import java.util.random.RandomGenerator;
import jdk.test.lib.RandomFactory;
public class RandomNextDoubleBoundary {
public static void main(String... args) {
negativeBounds();
positiveBounds();
nextDoubleHugeRange();
}
private static void negativeBounds() {
// Both bounds are negative
double lowerBound = -1.0000000000000002;
double upperBound = -1.0;
var sr = new SplittableRandom(42L);
var r = sr.nextDouble(lowerBound, upperBound);
if (r >= upperBound) {
System.err.println("r = " + r + "\t" + Double.toHexString(r));
System.err.println("ub = " + upperBound + "\t" + Double.toHexString(upperBound));
throw new RuntimeException("Greater than upper bound");
}
if (r < lowerBound) {
System.err.println("r = " + r + "\t" + Double.toHexString(r));
System.err.println("lb = " + lowerBound + "\t" + Double.toHexString(lowerBound));
throw new RuntimeException("Less than lower bound");
}
}
private static void positiveBounds() {
double[][] originAndBounds = {{10, 100},
{12345, 123456},
{5432167.234, 54321678.1238}};
for (double[] originAndBound : originAndBounds) {
nextDoublesWithRange(originAndBound[0], originAndBound[1]);
}
}
public static void nextDoublesWithRange(double origin, double bound) {
RandomGenerator rg = new RandomGenerator() {
@Override
public double nextDouble() {
return Double.MAX_VALUE;
}
@Override
public long nextLong() {
return 0;
}
};
double value = rg.nextDouble(origin, bound);
if (bound > 0) {
value = rg.nextDouble(bound); // Equivalent to nextDouble(0.0, bound)
assertTrue(value >= 0.0);
assertTrue(value < bound);
}
}
public static void nextDoubleHugeRange() {
var random = RandomFactory.getRandom();
var n = 100_000;
var origin = -(3.0 / 4.0) * Double.MAX_VALUE;
var bound = (3.0 / 4.0) * Double.MAX_VALUE;
assertTrue(bound - origin == Double.POSITIVE_INFINITY);
/* all are within [origin, bound) */
assertTrue(random.doubles(n, origin, bound)
.allMatch(d -> origin <= d && d < bound));
/* some are near the origin */
assertTrue(random.doubles(n, origin, bound)
.anyMatch(d -> d < (15.0 / 16.0) * origin));
/* some are near the bound */
assertTrue(random.doubles(n, origin, bound)
.anyMatch(d -> d > (15.0 / 16.0) * bound));
}
public static void assertTrue(boolean condition) {
if (!condition) {
throw new AssertionError();
}
}
}

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.testng.Assert.*;
/**
* @test
* @run testng RandomStreamTest
* @summary test stream methods on Random
* @author Brian Goetz
* @key randomness
*/
public class RandomStreamTest {
private static final int SIZE = 1000;
@DataProvider(name = "suppliers")
public Object[][] randomSuppliers() {
return new Object[][] {
{new Random(), SIZE},
{new SecureRandom(), SIZE}
};
}
@Test(dataProvider = "suppliers")
public void testRandomIntStream(final Random random, final int count) {
final List<Integer> destination = new ArrayList<>(count);
random.ints().limit(count).forEach(destination::add);
assertEquals(destination.size(), count);
}
@Test(dataProvider = "suppliers")
public void testRandomLongStream(final Random random, final int count) {
final List<Long> destination = new ArrayList<>(count);
random.longs().limit(count).forEach(destination::add);
assertEquals(destination.size(), count);
}
@Test(dataProvider = "suppliers")
public void testRandomDoubleStream(final Random random, final int count) {
final List<Double> destination = new ArrayList<>(count);
random.doubles().limit(count).forEach(destination::add);
random.doubles().limit(count).forEach(d -> assertTrue(d >= 0.0 && d < 1.0));
assertEquals(destination.size(), count);
}
@Test
public void testIntStream() {
final long seed = System.currentTimeMillis();
final Random r1 = new Random(seed);
final int[] a = new int[SIZE];
for (int i=0; i < SIZE; i++) {
a[i] = r1.nextInt();
}
final Random r2 = new Random(seed); // same seed
final int[] b = r2.ints().limit(SIZE).toArray();
assertEquals(a, b);
}
@Test
public void testLongStream() {
final long seed = System.currentTimeMillis();
final Random r1 = new Random(seed);
final long[] a = new long[SIZE];
for (int i=0; i < SIZE; i++) {
a[i] = r1.nextLong();
}
final Random r2 = new Random(seed); // same seed
final long[] b = r2.longs().limit(SIZE).toArray();
assertEquals(a, b);
}
@Test
public void testDoubleStream() {
final long seed = System.currentTimeMillis();
final Random r1 = new Random(seed);
final double[] a = new double[SIZE];
for (int i=0; i < SIZE; i++) {
a[i] = r1.nextDouble();
}
final Random r2 = new Random(seed); // same seed
final double[] b = r2.doubles().limit(SIZE).toArray();
assertEquals(a, b);
}
@Test
public void testThreadLocalIntStream() throws InterruptedException, ExecutionException, TimeoutException {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
testRandomResultSupplierConcurrently(() -> tlr.ints().limit(SIZE).boxed().collect(toList()));
}
@Test
public void testThreadLocalLongStream() throws InterruptedException, ExecutionException, TimeoutException {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
testRandomResultSupplierConcurrently(() -> tlr.longs().limit(SIZE).boxed().collect(toList()));
}
@Test
public void testThreadLocalDoubleStream() throws InterruptedException, ExecutionException, TimeoutException {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
testRandomResultSupplierConcurrently(() -> tlr.doubles().limit(SIZE).boxed().collect(toList()));
}
<T> void testRandomResultSupplierConcurrently(Supplier<T> s) throws InterruptedException, ExecutionException, TimeoutException {
// Produce 10 completable future tasks
final int tasks = 10;
List<CompletableFuture<T>> cfs = Stream.generate(() -> CompletableFuture.supplyAsync(s)).
limit(tasks).collect(toList());
// Wait for all tasks to complete
// Timeout is beyond reasonable doubt that completion should
// have occurred unless there is an issue
CompletableFuture<Void> all = CompletableFuture.allOf(cfs.stream().toArray(CompletableFuture[]::new));
all.get(1, TimeUnit.MINUTES);
// Count the distinct results, which should equal the number of tasks
long rc = cfs.stream().map(CompletableFuture::join).distinct().count();
assertEquals(rc, tasks);
}
}

View file

@ -0,0 +1,495 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.BiConsumer;
import java.util.random.RandomGenerator;
import java.util.ArrayList;
import java.util.Collections;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* @test
* @run testng RandomTest
* @summary test methods on Random
* @bug 8288596
* @key randomness
*/
@Test
public class RandomTest {
// Note: this test was adapted from the 166 TCK ThreadLocalRandomTest test
// and modified to be a TestNG test
/*
* Testing coverage notes:
*
* We don't test randomness properties, but only that repeated
* calls, up to NCALLS tries, produce at least one different
* result. For bounded versions, we sample various intervals
* across multiples of primes.
*/
// max numbers of calls to detect getting stuck on one value
static final int NCALLS = 10000;
// max sampled int bound
static final int MAX_INT_BOUND = (1 << 28);
// max sampled long bound
static final long MAX_LONG_BOUND = (1L << 42);
// Number of replications for other checks
static final int REPS = 20;
/**
* Repeated calls to nextInt produce at least two distinct results
*/
public void testNextInt() {
Random r = new Random();
int f = r.nextInt();
int i = 0;
while (i < NCALLS && r.nextInt() == f)
++i;
assertTrue(i < NCALLS);
}
/**
* Repeated calls to nextLong produce at least two distinct results
*/
public void testNextLong() {
Random r = new Random();
long f = r.nextLong();
int i = 0;
while (i < NCALLS && r.nextLong() == f)
++i;
assertTrue(i < NCALLS);
}
/**
* Repeated calls to nextBoolean produce at least two distinct results
*/
public void testNextBoolean() {
Random r = new Random();
boolean f = r.nextBoolean();
int i = 0;
while (i < NCALLS && r.nextBoolean() == f)
++i;
assertTrue(i < NCALLS);
}
/**
* Repeated calls to nextFloat produce at least two distinct results
*/
public void testNextFloat() {
Random r = new Random();
float f = r.nextFloat();
int i = 0;
while (i < NCALLS && r.nextFloat() == f)
++i;
assertTrue(i < NCALLS);
}
/**
* Repeated calls to nextDouble produce at least two distinct results
*/
public void testNextDouble() {
Random r = new Random();
double f = r.nextDouble();
int i = 0;
while (i < NCALLS && r.nextDouble() == f)
++i;
assertTrue(i < NCALLS);
}
/**
* Repeated calls to nextGaussian produce at least two distinct results
*/
public void testNextGaussian() {
Random r = new Random();
double f = r.nextGaussian();
int i = 0;
while (i < NCALLS && r.nextGaussian() == f)
++i;
assertTrue(i < NCALLS);
}
/**
* nextInt(negative) throws IllegalArgumentException
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNextIntBoundedNeg() {
Random r = new Random();
int f = r.nextInt(-17);
}
/**
* nextInt(bound) returns 0 <= value < bound; repeated calls produce at
* least two distinct results
*/
public void testNextIntBounded() {
Random r = new Random();
// sample bound space across prime number increments
for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
int f = r.nextInt(bound);
assertTrue(0 <= f && f < bound);
int i = 0;
int j;
while (i < NCALLS &&
(j = r.nextInt(bound)) == f) {
assertTrue(0 <= j && j < bound);
++i;
}
assertTrue(i < NCALLS);
}
}
/**
* Invoking sized ints, long, doubles, with negative sizes throws
* IllegalArgumentException
*/
public void testBadStreamSize() {
Random r = new Random();
assertThrowsIAE(() -> r.ints(-1L));
assertThrowsIAE(() -> r.ints(-1L, 2, 3));
assertThrowsIAE(() -> r.longs(-1L));
assertThrowsIAE(() -> r.longs(-1L, -1L, 1L));
assertThrowsIAE(() -> r.doubles(-1L));
assertThrowsIAE(() -> r.doubles(-1L, .5, .6));
}
/**
* Invoking bounded ints, long, doubles, with illegal bounds throws
* IllegalArgumentException
*/
public void testBadStreamBounds() {
Random r = new Random();
assertThrowsIAE(() -> r.ints(2, 1));
assertThrowsIAE(() -> r.ints(10, 42, 42));
assertThrowsIAE(() -> r.longs(-1L, -1L));
assertThrowsIAE(() -> r.longs(10, 1L, -2L));
testDoubleBadOriginBound((o, b) -> r.doubles(10, o, b));
}
// An arbitrary finite double value
static final double FINITE = Math.PI;
void testDoubleBadOriginBound(BiConsumer<Double, Double> bi) {
assertThrowsIAE(() -> bi.accept(17.0, 2.0));
assertThrowsIAE(() -> bi.accept(0.0, 0.0));
assertThrowsIAE(() -> bi.accept(Double.NaN, FINITE));
assertThrowsIAE(() -> bi.accept(FINITE, Double.NaN));
assertThrowsIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
// Returns NaN
// assertThrowsIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, FINITE));
// assertThrowsIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
assertThrowsIAE(() -> bi.accept(FINITE, Double.NEGATIVE_INFINITY));
// Returns Double.MAX_VALUE
// assertThrowsIAE(() -> bi.accept(FINITE, Double.POSITIVE_INFINITY));
assertThrowsIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
assertThrowsIAE(() -> bi.accept(Double.POSITIVE_INFINITY, FINITE));
assertThrowsIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
}
private void assertThrowsIAE(ThrowingRunnable r) {
assertThrows(IllegalArgumentException.class, r);
}
/**
* A sequential sized stream of ints generates the given number of values
*/
public void testIntsCount() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 0;
for (int reps = 0; reps < REPS; ++reps) {
counter.reset();
r.ints(size).forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
size += 524959;
}
}
/**
* A sequential sized stream of longs generates the given number of values
*/
public void testLongsCount() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 0;
for (int reps = 0; reps < REPS; ++reps) {
counter.reset();
r.longs(size).forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
size += 524959;
}
}
/**
* A sequential sized stream of doubles generates the given number of values
*/
public void testDoublesCount() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 0;
for (int reps = 0; reps < REPS; ++reps) {
counter.reset();
r.doubles(size).forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
size += 524959;
}
}
/**
* Each of a sequential sized stream of bounded ints is within bounds
*/
public void testBoundedInts() {
AtomicInteger fails = new AtomicInteger(0);
Random r = new Random();
long size = 12345L;
for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
final int lo = least, hi = bound;
r.ints(size, lo, hi).
forEach(x -> {
if (x < lo || x >= hi)
fails.getAndIncrement();
});
}
}
assertEquals(fails.get(), 0);
}
/**
* Each of a sequential sized stream of bounded longs is within bounds
*/
public void testBoundedLongs() {
AtomicInteger fails = new AtomicInteger(0);
Random r = new Random();
long size = 123L;
for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
final long lo = least, hi = bound;
r.longs(size, lo, hi).
forEach(x -> {
if (x < lo || x >= hi)
fails.getAndIncrement();
});
}
}
assertEquals(fails.get(), 0);
}
/**
* Each of a sequential sized stream of bounded doubles is within bounds
*/
public void testBoundedDoubles() {
AtomicInteger fails = new AtomicInteger(0);
Random r = new Random();
long size = 456;
for (double least = 0.00011; least < 1.0e20; least *= 9) {
for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
final double lo = least, hi = bound;
r.doubles(size, lo, hi).
forEach(x -> {
if (x < lo || x >= hi)
fails.getAndIncrement();
});
}
}
assertEquals(fails.get(), 0);
}
/**
* A parallel unsized stream of ints generates at least 100 values
*/
public void testUnsizedIntsCount() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 100;
r.ints().limit(size).parallel().forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
}
/**
* A parallel unsized stream of longs generates at least 100 values
*/
public void testUnsizedLongsCount() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 100;
r.longs().limit(size).parallel().forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
}
/**
* A parallel unsized stream of doubles generates at least 100 values
*/
public void testUnsizedDoublesCount() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 100;
r.doubles().limit(size).parallel().forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
}
/**
* A sequential unsized stream of ints generates at least 100 values
*/
public void testUnsizedIntsCountSeq() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 100;
r.ints().limit(size).forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
}
/**
* A sequential unsized stream of longs generates at least 100 values
*/
public void testUnsizedLongsCountSeq() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 100;
r.longs().limit(size).forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
}
/**
* A sequential unsized stream of doubles generates at least 100 values
*/
public void testUnsizedDoublesCountSeq() {
LongAdder counter = new LongAdder();
Random r = new Random();
long size = 100;
r.doubles().limit(size).forEach(x -> {
counter.increment();
});
assertEquals(counter.sum(), size);
}
/**
* Test shuffling a list with Random.from()
*/
public void testShufflingList() {
final var listTest = new ArrayList<Integer>();
final RandomGenerator randomGenerator = RandomGenerator.getDefault();
final Random random = Random.from(randomGenerator);
for (int i = 0; i < 100; i++) {
listTest.add(i * 2);
}
final var listCopy = new ArrayList<Integer>(listTest);
Collections.shuffle(listCopy, random);
assertFalse(listCopy.equals(listTest));
}
/**
* Test if Random.from returns this
*/
public void testRandomFromInstance() {
final RandomGenerator randomGenerator = RandomGenerator.getDefault();
final Random randomInstance = Random.from(randomGenerator);
// we wrap the same instance again
final Random randomInstanceCopy = Random.from(randomInstance);
assertSame(randomInstance, randomInstanceCopy);
}
private int delegationCount;
private class RandomGen implements RandomGenerator {
@Override
public boolean isDeprecated() {
delegationCount += 1;
return RandomGenerator.super.isDeprecated();
}
@Override
public float nextFloat(float bound) {
delegationCount += 1;
return RandomGenerator.super.nextFloat(bound);
}
@Override
public double nextDouble(double bound) {
delegationCount += 1;
return RandomGenerator.super.nextDouble(bound);
}
@Override
public long nextLong() {
return 0;
}
}
/*
* Test whether calls to methods inherited from RandomGenerator
* are delegated to the instance returned by from().
* This is not a complete coverage, but simulates the reproducer
* in issue JDK-8288596
*/
public void testRandomFrom() {
delegationCount = 0;
var r = Random.from(new RandomGen());
r.isDeprecated();
r.nextFloat(1_000.0f);
r.nextFloat(); // not implemented in RandomGen, does not count
r.nextDouble(1_000.0);
r.nextDouble(); // not implemented in RandomGen, does not count
assertEquals(delegationCount, 3);
}
}

View file

@ -0,0 +1,456 @@
/*
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.ArrayList;
import java.util.List;
import java.util.PrimitiveIterator;
import java.util.random.*;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
/**
* @test
* @summary test bit sequences produced by classes that implement interface RandomGenerator
* @bug 8248862
* @run main/othervm/timeout=400 RandomTestBsi1999
* @key randomness
*/
public class RandomTestBsi1999 {
/* A set of tests for pseudorandom number generators inspired by this report:
*
* Werner Schindler. Functionality Classes and Evaluation Methodology for
* Deterministic Random Number Generators, Version 2.0.
* Bundesamt fur Sicherheit in der Informationstechnik (BSI). December 2, 1999.
* https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Zertifizierung/Interpretationen/AIS_20_Functionality_Classes_Evaluation_Methodology_DRNG_e.pdf
*
* Section F of this report (pp. 19-20) recommends the use of five tests to evaluate a
* sequence of bits:
*
* Monobit test
* Poker test
* Run test
* Long run test
* Autocorrelation test
*
* The first four of these tests are in turn taken from this report:
*
* National Institute of Standards and Technology (NIST),
* U.S. Department of Commerce. Security Requirements for
* Cryptographic Modules. Federal Information Processing
* Standard (FIPS) 140-1, January 11, 1994.
* https://csrc.nist.gov/csrc/media/publications/fips/140/1/archive/1994-01-11/documents/fips1401.pdf
*
* The BSI report appears to contain a few typos in transcribing the FIPS 140-1
* requirements (pp. 44-45); specifically, the last three intervals for the runs test
* (for lengths 4, 5, and 6+) are given as "223 - 402, 90 - 223, 90 - 223" in the FIPS
* standard but as "233-402, 90-223, 90-233" in the BSI publication. A quick
* mathematical check indicates that the FIPS 140-1 values are correct; therefore we
* use those values here. In addition, the BSI report specifies a test interval of
* 2326-2674 for the autocorrelation test, which provides an appropriately small
* rejection rate if the test were done for only a single value of tau; but because we
* wish to perform 5000 distinct tests, one for each value of tau in the range 1-5000,
* that test interval produces too many false positives. Some calculation shows that
* the interval 2267-2733 used by the FIPS 140-1 run test for runs of length 1 is
* appropriate, so we use that interval here for each of the 5000 individual
* autocorrelation tests.
*
* Each of the four FIPS 140-1 tests examines a sequence of 20000 bits. The
* autocorrelation test examines a sequence of 10000 bits. It is convenient to
* generate a sequence of 20000 bits (which we represent as an array of 20000 bytes)
* and then apply all five tests, knowing that the autocorrelation test will examine
* only the first half of the byte array.
*
* The descriptions of the tests are quoted from the FIPS 140-1 and BSI reports
* (with some adjustments of punctuation and mathematical symbols, as well as
* for our specific choices of test intervals).
*/
static String currentRNG = "";
static int failCount = 0;
static void exceptionOnFail() {
if (failCount != 0) {
throw new RuntimeException(failCount + " fails detected");
}
}
static void setRNG(String rng) {
currentRNG = rng;
}
static void fail(String format, Object... args) {
if (currentRNG.length() != 0) {
System.err.println(currentRNG);
currentRNG = "";
}
System.err.format(" " + format, args);
failCount++;
}
private static final int SEQUENCE_SIZE = 20000;
/* The Monobit Test
*
* 1. Count the number of ones in the 20,000 bit stream. Denote this quantity by X.
*
* 2. The test is passed if 9,654 < X < 10,346.
*/
static int monobitTest(String id, byte[] s) {
// System.out.println("monobit test");
int count = 0;
for (int j = 0; j < s.length; j++) {
count += s[j];
}
int monobitFailure = ((9654 < count) && (count < 10346)) ? 0 : 1;
if (monobitFailure != 0) fail("monobit test failure for %s: count=%d (should be in [9654,10346])\n", id, count);
return monobitFailure;
}
/* The Poker Test
*
* 1. Divide the 20,000 bit stream into 5,000 contiguous 4-bit segments. Count and
* store the number of occurrences of each of the 16 possible 4-bit values. Denote
* f(i) as the number of each 4-bit value i where 0 <= i <= 15.
*
* 2. Evaluate the following: X = (16/5000)(sum[i=0,15] (f(i))**2) - 5000
*
* 3. The test is passed if 1.03 < X < 57.4.
*/
static int pokerTest(String id, byte[] s) {
// System.out.println("poker test");
// Divide the bit sequence into 4-bit chunks, and count the number of times each 4-bit value appears.
int[] stats = new int[16];
int v = 0;
for (int j = 0; j < s.length; j++) {
v = (v << 1) | s[j];
if ((j & 3) == 3) {
++stats[v];
v = 0;
}
}
int z = 0;
for (int k = 0; k < stats.length; k++) {
z += stats[k]*stats[k];
}
double x = (16.0 / (s.length / 4)) * z - (s.length / 4);
int pokerFailure = ((1.03 < x) && (x < 57.4)) ? 0 : 1;
if (pokerFailure != 0) fail("poker test failure for %s: x=%g (should be in [1.03,57.4])\n", id, x);
return pokerFailure;
}
/* The Runs Test
*
* 1. A run is defined as a maximal sequence of consecutive bits of either all ones
* or all zeros, which is part of the 20,000 bit sample stream. The incidences of
* runs (for both consecutive zeros and consecutive ones) of all lengths (>= 1) in
* the sample stream should be counted and stored.
*
* 2. The test is passed if the number of runs that occur (of lengths 1 through 6)
* is each within the corresponding interval specified below. This must hold for
* both the zeros and ones; that is, all 12 counts must lie in the specified
* interval. For the purpose of this test, runs of greater than 6 are considered to
* be of length 6.
*
* Length of run Required Interval
* 1 2,267 - 2,733
* 2 1,079 - 1,421
* 3 502 - 748
* 4 223 - 402
* 5 90 - 223
* 6+ 90 - 223
*
* The Long Run Test
*
* 1 . A long run is defined to be a run of length 34 or more (of either zeros or ones).
*
* 2. On the sample of 20,000 bits, the test is passed if there are NO long runs.
*/
static int runTestAndLongRunTest(String id, byte[] s) {
// System.out.println("run test");
int[][] stats = new int[2][8];
int count = 0;
for (int j = 0; j < s.length; j++) {
++count;
if ((j == (s.length - 1)) || (s[j+1] != s[j])) {
++stats[s[j]][(count < 6) ? count : (count < 34) ? 6 : 7];
count = 0;
}
}
stats[0][6] += stats[0][7];
stats[1][6] += stats[1][7];
int runFailure = checkRunStats(stats[0]) | checkRunStats(stats[1]);
if (runFailure != 0) fail("run test failure for %s\n", id);
int longRunFailure = ((stats[0][7] == 0) && (stats[1][7] == 0)) ? 0 : 1;
if (longRunFailure != 0) fail("long run test failure for %s\n", id);
return (runFailure + longRunFailure);
}
static int checkRunStats(int[] stats) {
int runFailure = 0;
runFailure |= ((2267 <= stats[1]) && (stats[1] <= 2733)) ? 0 : 1;
runFailure |= ((1079 <= stats[2]) && (stats[2] <= 1421)) ? 0 : 1;
runFailure |= (( 502 <= stats[3]) && (stats[3] <= 748)) ? 0 : 1;
runFailure |= (( 223 <= stats[4]) && (stats[4] <= 402)) ? 0 : 1;
runFailure |= (( 90 <= stats[5]) && (stats[5] <= 223)) ? 0 : 1;
runFailure |= (( 90 <= stats[6]) && (stats[6] <= 223)) ? 0 : 1;
return runFailure;
}
/* Autocorrelation Test
*
* For tau in {1, ..., 5000}, Z[tau] := sum[j=1,5000] (b[j] ^ b[j+tau]).
*
* The sequence passes the autocorrelation test if every Z[tau] lies within the
* interval 2267-2733.
*/
static int autocorrelationTest(String id, byte[] s) {
// System.out.println("autocorrelation test");
int autocorrelationFailure = 0;
int N = s.length / 4;
for (int tau = 1; tau <= N; tau++) {
int count = 0;
for (int j = 0; j < N; j++) {
count += (s[j] ^ s[j+tau]);
}
// We intentionally use bounds [2267, 2733], which are wider than
// the bounds [2326, 2674] specified by BSI for this test.
// The latter bounds produce way too many false positives.
int singleAutocorrelationFailure = ((2267 < count) && (count < 2733)) ? 0 : 1;
if (singleAutocorrelationFailure != 0) {
if (autocorrelationFailure < 8) {
fail("autocorrelation failure for %s: count=%d (should be in [2267,2733]), tau=%d\n", id, count, tau);
if (count < 100 || count > 4900) {
System.out.print(" ");
for (int q = 0; q < 50; q++) System.out.print(s[q]);
System.out.println();
}
}
}
autocorrelationFailure += singleAutocorrelationFailure;
}
return (autocorrelationFailure == 0) ? 0 : 1;
}
static int entireBsi1999Test(String id, byte[] s) {
return (monobitTest(id, s) +
pokerTest(id, s) +
runTestAndLongRunTest(id, s) +
autocorrelationTest(id, s)
);
}
/* To test a sequence of boolean values from a BooleanSupplier,
* sequentially extract 20000 boolean values, convert to an array
* of bytes, and feed them to method {@code entireBsi1999Test}.
*/
static int testRngBsi1999BooleanOnce(String id, BooleanSupplier theSupplier) {
int failureCount = 0;
byte[] s = new byte[SEQUENCE_SIZE];
// Take the next SEQUENCE_SIZE booleans and test them
for (int j = 0; j < s.length; j++) {
s[j] = (theSupplier.getAsBoolean() ? (byte)1 : (byte)0);
}
failureCount += entireBsi1999Test(id + " consecutive", s);
return failureCount;
}
/* To test a sequence of long values from a LongSupplier,
* two kinds of tests are performed.
*
* The first kind of test extracts 313=ceiling(20000/64) long
* values and concatenates all their bits; the first 20000 bits
* are converted to a byte array of bits to be tested. This test is
* repeated 64 times.
*
* The second kind of test focuses on one bit position m (0 <= m < 64);
* it extracts 20000 long values and uses just bit m from each value
* to produce an array of bytes to be tested. This test is performed
* once for each possible value of m (64 times in all).
*/
static int testRngBsi1999LongOnce(String id, LongSupplier theSupplier) {
int failureCount = 0;
byte[] s = new byte[SEQUENCE_SIZE];
// Part 1: 64 times, take the next SEQUENCE_SIZE bits and test them
for (int m = 0; m < 64; m++) {
long bits = 0;
int bitCount = 0;
for (int j = 0; j < s.length; j++) {
if ((j & 0x3f) == 0) {
bits = theSupplier.getAsLong();
// System.out.printf("0x%016x\n", bits);
bitCount += Long.bitCount((j == (20000 - 32)) ? ((bits << 32) >>> 32) : bits);
}
s[j] = (byte)(bits & 1);
bits >>>= 1;
}
// System.out.println(m + ": " + bitCount + " 1-bits");
failureCount += entireBsi1999Test(id + " consecutive (" + bitCount + " 1-bits)", s);
}
// Part 2: for 0 <= m < 64, use bit m from each of the next SEQUENCE_SIZE longs
for (int m = 0; m < 64; m++) {
for (int j = 0; j < s.length; j++) {
s[j] = (byte)((theSupplier.getAsLong() >>> m) & 1);
}
failureCount += entireBsi1999Test(id + " bit " + m, s);
}
return failureCount;
}
/* To test a sequence of ing values from an IntSupplier,
* two kinds of tests are performed.
*
* The first kind of test extracts 625=20000/32 int values and
* concatenates all their bits; these 20000 bits are converted to
* a byte array of bits to be tested. This test is repeated 64
* times.
*
* The second kind of test focuses on one bit position m (0 <= m < 32);
* it extracts 20000 int values and uses just bit m from each value
* to produce an array of bytes to be tested. This test is performed
* once for each possible value of m (32 times in all).
*/
static int testRngBsi1999IntOnce(String id, IntSupplier theSupplier) {
int failureCount = 0;
byte[] s = new byte[SEQUENCE_SIZE];
// Part 1: 64 times, take the next SEQUENCE_SIZE bits and test them
for (int m = 0; m < 64; m++) {
int bits = 0;
int bitCount = 0;
for (int j = 0; j < s.length; j++) {
if ((j & 0x1f) == 0) {
bits = theSupplier.getAsInt();
bitCount += Integer.bitCount(bits);
}
s[j] = (byte)(bits & 1);
bits >>>= 1;
}
// System.out.println(m + ": " + bitCount + " 1-bits");
failureCount += entireBsi1999Test(id + " consecutive (" + bitCount + " 1-bits)", s);
}
// Part 2: for 0 <= m < 32, use bit m from each of the next SEQUENCE_SIZE ints
for (int m = 0; m < 32; m++) {
for (int j = 0; j < s.length; j++) {
s[j] = (byte)((theSupplier.getAsInt() >>> m) & 1);
}
failureCount += entireBsi1999Test(id + " bit " + m, s);
}
return failureCount;
}
/* A call to {@code entireBsi1999Test} may report failure even if the source of random
* bits is quite good, because the test is statistical in nature. To make the testing
* procedure more robust, if the first call to {@code entireBsi1999Test} fails, then
* the failure is ignored if two more calls to {@code entireBsi1999Test} both succeed.
*/
static boolean testRngBsi1999Boolean(String id, BooleanSupplier theSupplier, int failureTolerance) {
if (testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance) return true;
fail("testRngBsi1999Boolean glitch");
return ((testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance) &&
(testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance));
}
static boolean testRngBsi1999Long(String id, LongSupplier theSupplier, int failureTolerance) {
if (testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance) return true;
fail("testRngBsi1999Long glitch");
return ((testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance) &&
(testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance));
}
static boolean testRngBsi1999Int(String id, IntSupplier theSupplier, int failureTolerance) {
if (testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance) return true;
fail("testRngBsi1999Int glitch");
return ((testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance) &&
(testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance));
}
static void tryIt(RandomGenerator rng, String id, BooleanSupplier theSupplier) {
System.out.printf("Testing %s %s\n", rng.getClass().getName(), id);
boolean success = theSupplier.getAsBoolean();
if (!success) {
fail("*** Failure of %s %s\n", rng.getClass().getName(), id);
}
}
static void testOneRng(RandomGenerator rng, int failureTolerance) {
String name = rng.getClass().getName();
tryIt(rng, "nextInt", () -> testRngBsi1999Int(name + " nextInt", rng::nextInt, failureTolerance));
tryIt(rng, "nextLong", () -> testRngBsi1999Long(name + " nextLong", rng::nextLong, failureTolerance));
tryIt(rng, "nextBoolean", () -> testRngBsi1999Boolean(name + " nextBoolean", rng::nextBoolean, failureTolerance));
tryIt(rng, "ints", () -> testRngBsi1999Int(name + " ints", rng.ints().iterator()::next, failureTolerance));
tryIt(rng, "longs", () -> testRngBsi1999Long(name + " longs", rng.longs().iterator()::next, failureTolerance));
{
PrimitiveIterator.OfDouble iter = rng.doubles().iterator();
tryIt(rng, "doubles",
() -> testRngBsi1999Int(name + " doubles",
() -> (int)(long)Math.floor(iter.next() * 0x1.0p32),
failureTolerance));
}
tryIt(rng, "nextDouble",
() -> testRngBsi1999Int(name + " nextDouble",
() -> (int)(long)Math.floor(rng.nextDouble() * 0x1.0p32),
failureTolerance));
tryIt(rng, "nextFloat",
() -> testRngBsi1999Int(name + " nextFloat",
() -> (((int)(long)Math.floor(rng.nextFloat() * 0x1.0p16f) << 16)
| (int)(long)Math.floor(rng.nextFloat() * 0x1.0p16f)),
failureTolerance));
}
public static void main(String[] args) {
RandomGeneratorFactory.all().forEach(factory -> {
setRNG(factory.name());
if (currentRNG.equals("SecureRandom")) {
// skip because stochastic
} else if (currentRNG.equals("Random")) {
// testOneRng(factory.create(59), 1);
// autocorrelation failure for java.util.Random longs bit 0: count=2207 (should be in [2267,2733]), tau=2819
} else {
testOneRng(factory.create(59), 0);
}
});
exceptionOnFail();
}
}

View file

@ -0,0 +1,243 @@
/*
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.ArrayList;
import java.util.List;
import java.util.PrimitiveIterator;
import java.util.random.*;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
/**
* @test
* @summary test bit sequences produced by clases that implement interface RandomGenerator
* @bug 8248862
* @run main RandomTestChiSquared
* @key randomness
*/
public class RandomTestChiSquared {
static String currentRNG = "";
static int failCount = 0;
static void exceptionOnFail() {
if (failCount != 0) {
throw new RuntimeException(failCount + " fails detected");
}
}
static void setRNG(String rng) {
currentRNG = rng;
}
static void fail(String format, Object... args) {
if (currentRNG.length() != 0) {
System.err.println(currentRNG);
currentRNG = "";
}
System.err.format(" " + format, args);
failCount++;
}
// Some simple chi-squared tests similar to that used for the FIPS 140 poker test,
// but intended primarily to test production of random values from ranges.
private static final int SEQUENCE_SIZE = 20000;
// Entry k of this table was computed in Microsoft Excel using the formulae
// =CHISQ.INV(0.0000000777517,k) and =CHISQ.INV.RT(0.00000142611,k)
// (except that entry 0 is a dummy and should never be used).
static final double[][] chiSquaredBounds = {
{ 0.0, 0.0 },
{ 9.49598E-15, 23.24513154 },
{ 1.55503E-07, 26.92112020 },
{ 4.40485E-05, 29.93222467 },
{ 0.000788782, 32.62391510 },
{ 0.004636947, 35.11665045 },
{ 0.015541535, 37.46947634 },
{ 0.037678318, 39.71667636 },
{ 0.074471919, 41.88031518 },
{ 0.128297057, 43.97561646 },
{ 0.200566433, 46.01362542 },
{ 0.291944952, 48.00266676 },
{ 0.402564694, 49.94920504 },
{ 0.532199236, 51.85838271 },
{ 0.680392565, 53.73437242 },
{ 0.846549931, 55.58061674 },
{ 1.030000010, 57.39999630 },
{ 1.230036580, 59.19495111 },
{ 1.445945898, 60.96756998 },
{ 1.677024296, 62.71965780 },
{ 1.922589129, 64.45278706 },
{ 2.181985238, 66.16833789 },
{ 2.454588423, 67.86752964 },
{ 2.739806923, 69.55144602 },
{ 3.037081611, 71.22105544 },
{ 3.345885349, 72.87722754 },
{ 3.665721841, 74.52074682 },
{ 3.996124178, 76.15232388 },
{ 4.336653242, 77.77260487 },
{ 4.686896041, 79.38217943 },
{ 5.046464051, 80.98158736 },
{ 5.414991603, 82.57132439 }
};
// N is the number of categories; every element of s ought to be in [0,N).
// N must be in [1,31].
static boolean chiSquaredTest(String id, int N, IntSupplier theSupplier) {
int[] stats = new int[N];
for (int j = 0; j < SEQUENCE_SIZE; j++) {
int v = theSupplier.getAsInt();
// assert((0 <= v) && (v < N));
++stats[v];
}
int z = 0;
for (int k = 0; k < stats.length; k++) {
z += stats[k]*stats[k];
}
double x = ((double)N / (double)SEQUENCE_SIZE) * (double)z - (double)SEQUENCE_SIZE;
double lo = chiSquaredBounds[N][0];
double hi = chiSquaredBounds[N][1];
boolean chiSquaredSuccess = ((lo < x) && (x < hi));
if (!chiSquaredSuccess) fail("chi-squared test failure for %s: x=%g (should be in [%g,%g])\n", id, x, lo, hi);
return chiSquaredSuccess;
}
static boolean testRngChiSquared(String id, int N, IntSupplier theSupplier) {
if (chiSquaredTest(id, N, theSupplier)) return true;
fail("testRngChiSquared glitch");
return chiSquaredTest(id, N, theSupplier) && chiSquaredTest(id, N, theSupplier);
}
static void tryIt(RandomGenerator rng, String kind, Function<String,BooleanSupplier> f) {
String id = rng.getClass().getName() + " " + kind;
// System.out.printf("Testing %s\n", id);
boolean success = f.apply(id).getAsBoolean();
if (!success) {
fail("*** Failure of %s\n", id);
}
}
// To test one instance of an RandomGenerator with the BSI test suite, we test:
// nextInt()
// nextLong()
// nextBoolean()
// ints()
// longs()
// doubles()
// nextDouble()
// nextFloat()
static final int[] testSizes = { 11, 13, 16, 17, 19, 23, 24 };
static void testOneRng(RandomGenerator rng, int failureTolerance) {
String name = rng.getClass().getName();
for (int j = 0; j < testSizes.length; j++) {
int N = testSizes[j];
tryIt(rng, "nextInt(" + N + ")", (String id) -> () -> testRngChiSquared(id, N, () -> rng.nextInt(N)));
tryIt(rng, "nextInt(43, " + (N+43) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> rng.nextInt(43, N+43) - 43));
tryIt(rng, "nextInt(-59, " + (N-59) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> rng.nextInt(-59, N-59) + 59));
}
for (int j = 0; j < testSizes.length; j++) {
int N = testSizes[j];
tryIt(rng, "nextLong(" + N + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextLong(N))));
tryIt(rng, "nextLong(43, " + (N+43) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextLong(43, N+43) - 43)));
tryIt(rng, "nextLong(-59, " + (N-59) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextLong(-59, N-59) + 59)));
}
for (int j = 0; j < testSizes.length; j++) {
int N = testSizes[j];
tryIt(rng, "nextFloat(" + N + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextFloat(N)) % N));
tryIt(rng, "nextFloat(43, " + (N+43) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextFloat(43, N+43) - 43) % N));
tryIt(rng, "nextFloat(-59, " + (N-59) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextFloat(-59, N-59) + 59) % N));
}
for (int j = 0; j < testSizes.length; j++) {
int N = testSizes[j];
tryIt(rng, "nextDouble(" + N + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextDouble(N)) % N));
tryIt(rng, "nextDouble(43, " + (N+43) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextDouble(43, N+43) - 43) % N));
tryIt(rng, "nextDouble(-59, " + (N-59) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(rng.nextDouble(-59, N-59) + 59) % N));
}
for (int j = 0; j < testSizes.length; j++) {
int N = testSizes[j];
PrimitiveIterator.OfInt iter1 = rng.ints(0, N).iterator();
tryIt(rng, "ints(0, " + N + ")", (String id) -> () -> testRngChiSquared(id, N, iter1::next));
PrimitiveIterator.OfInt iter2 = rng.ints(43, N+43).iterator();
tryIt(rng, "ints(43, " + (N+43) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> iter2.next() - 43));
PrimitiveIterator.OfInt iter3 = rng.ints(-59, N-59).iterator();
tryIt(rng, "ints(-59, " + (N-59) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> iter3.next() + 59));
}
for (int j = 0; j < testSizes.length; j++) {
int N = testSizes[j];
PrimitiveIterator.OfLong iter1 = rng.longs(0, N).iterator();
tryIt(rng, "longs(0, " + N + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(iter1.next() + 0)));
PrimitiveIterator.OfLong iter2 = rng.longs(43, N+43).iterator();
tryIt(rng, "longs(43, " + (N+43) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(iter2.next() - 43)));
PrimitiveIterator.OfLong iter3 = rng.longs(-59, N-59).iterator();
tryIt(rng, "longs(-59, " + (N-59) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(iter3.next() + 59)));
}
for (int j = 0; j < testSizes.length; j++) {
int N = testSizes[j];
PrimitiveIterator.OfDouble iter1 = rng.doubles(0, N).iterator();
tryIt(rng, "doubles(0, " + N + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(iter1.next() + 0) % N));
PrimitiveIterator.OfDouble iter2 = rng.doubles(43, N+43).iterator();
tryIt(rng, "doubles(43, " + (N+43) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(iter2.next() - 43) % N));
PrimitiveIterator.OfDouble iter3 = rng.doubles(-59, N-59).iterator();
tryIt(rng, "doubles(-59, " + (N-59) + ")", (String id) -> () -> testRngChiSquared(id, N, () -> (int)(iter3.next() + 59) % N));
}
}
public static void main(String[] args) {
RandomGeneratorFactory.all()
.filter(f -> !f.name().equals("SecureRandom"))
.forEach(factory -> {
setRNG(factory.name());
if (factory.name().equals("Random")) {
testOneRng(factory.create(417), 1);
} else {
testOneRng(factory.create(417), 0);
}
});
exceptionOnFail();
}
}

View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.concurrent.ThreadLocalRandom;
import java.util.random.RandomGenerator;
import java.util.random.RandomGenerator.ArbitrarilyJumpableGenerator;
import java.util.random.RandomGenerator.JumpableGenerator;
import java.util.random.RandomGenerator.LeapableGenerator;
import java.util.random.RandomGenerator.SplittableGenerator;
import java.util.random.RandomGenerator.StreamableGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
/**
* @test
* @summary Ensure that all implementations of RandomGenerator supply required methods.
* @bug 8248862
* @run main RandomTestCoverage
* @key randomness
*/
public class RandomTestCoverage {
static void coverRandomGenerator(RandomGenerator rng) {
boolean bool = rng.nextBoolean();
byte[] bytes = new byte[8];
rng.nextBytes(bytes);
int i1 = rng.nextInt();
int i2 = rng.nextInt(10);
int i3 = rng.nextInt(5, 10);
long l1 = rng.nextLong();
long l2 = rng.nextLong(10L);
long l3 = rng.nextLong(5L, 10L);
float f1 = rng.nextFloat();
float f2 = rng.nextFloat(1.0f);
float f3 = rng.nextFloat(0.5f, 1.0f);
double d1 = rng.nextDouble();
double d2 = rng.nextDouble(1.0);
double d3 = rng.nextDouble(0.5, 1.0);
double exp = rng.nextExponential();
double gauss1 = rng.nextGaussian();
double gauss2 = rng.nextGaussian(0.5, 2.0);
IntStream intStream1 = rng.ints();
IntStream intStream2 = rng.ints(5, 10);
IntStream intStream3 = rng.ints(5L);
IntStream intStream4 = rng.ints(5L, 5, 10);
LongStream longStream1 = rng.longs();
LongStream longStream2 = rng.longs(5L, 10L);
LongStream longStream3 = rng.longs(5L);
LongStream longStream4 = rng.longs(5L, 5L, 10L);
DoubleStream doubleStream1 = rng.doubles();
DoubleStream doubleStream2 = rng.doubles(0.5, 1.0);
DoubleStream doubleStream3 = rng.doubles(5);
DoubleStream doubleStream4 = rng.doubles(5, 0.5, 1.0);
}
static void checkPredicates(RandomGeneratorFactory<RandomGenerator> factory) {
RandomGenerator rng = factory.create();
if (rng instanceof ArbitrarilyJumpableGenerator != factory.isArbitrarilyJumpable()) {
throw new RuntimeException("isArbitrarilyJumpable failing");
}
if (rng instanceof JumpableGenerator != factory.isJumpable()) {
throw new RuntimeException("isJumpable failing");
}
if (rng instanceof LeapableGenerator != factory.isLeapable()) {
throw new RuntimeException("isLeapable failing");
}
if (rng instanceof SplittableGenerator != factory.isSplittable()) {
throw new RuntimeException("isArbitrarilyJumpable failing");
}
if (rng instanceof StreamableGenerator != factory.isStreamable()) {
throw new RuntimeException("isArbitrarilyJumpable failing");
}
}
static void coverStreamable(StreamableGenerator rng) {
Stream<RandomGenerator> rngs1 = rng.rngs();
Stream<RandomGenerator> rngs2 = rng.rngs(5L);
}
static void coverSplittable(SplittableGenerator rng) {
SplittableGenerator s1 = rng.split();
SplittableGenerator s2 = rng.split(rng);
Stream<SplittableGenerator> s3 = rng.splits();
Stream<SplittableGenerator> s4 = rng.splits(5L);
Stream<SplittableGenerator> s5 = rng.splits(rng);
Stream<SplittableGenerator> s6 = rng.splits(5L, rng);
}
static void coverJumpable(JumpableGenerator rng) {
JumpableGenerator j1 = rng.copy();
rng.jump();
RandomGenerator j2 = rng.copyAndJump();
double d = rng.jumpDistance();
Stream<RandomGenerator> j3 = rng.jumps();
Stream<RandomGenerator> j4 = rng.jumps(5L);
}
static void coverLeapable(LeapableGenerator rng) {
LeapableGenerator l1 = rng.copy();
rng.leap();
JumpableGenerator l2 = rng.copyAndLeap();
double d = rng.leapDistance();
Stream<JumpableGenerator> l3 = rng.leaps();
Stream<JumpableGenerator> l4 = rng.leaps(5L);
}
static void coverArbitrarilyJumpable(ArbitrarilyJumpableGenerator rng) {
ArbitrarilyJumpableGenerator a1 = rng.copy();
rng.jump();
rng.leap();
rng.jump(1.2345);
rng.jumpPowerOfTwo(4);
RandomGenerator a2 = rng.copyAndJump();
RandomGenerator a3 = rng.copyAndJump(1.2345);
Stream<ArbitrarilyJumpableGenerator> a4 = rng.jumps(1.2345);
Stream<ArbitrarilyJumpableGenerator> a5 = rng.jumps(5L, 1.2345);
}
static void coverOf(String name) {
coverRandomGenerator(RandomGenerator.of(name));
coverFactory(RandomGeneratorFactory.of(name));
}
static void coverFactory(RandomGeneratorFactory<RandomGenerator> factory) {
String name = factory.name();
String group = factory.group();
int stateBits = factory.stateBits();
int equidistribution = factory.equidistribution();
BigInteger period = factory.period();
boolean isStatistical = factory.isStatistical();
boolean isStochastic = factory.isStochastic();
boolean isHardware = factory.isHardware();
boolean isArbitrarilyJumpable = factory.isArbitrarilyJumpable();
boolean isJumpable = factory.isJumpable();
boolean isLeapable = factory.isLeapable();
boolean isSplittable = factory.isSplittable();
coverRandomGenerator(factory.create());
// test create(long)
switch (factory.name()) {
// SecureRandom doesn't have long constructors so we expect
// UnsupportedOperationException
case "SecureRandom" -> {
try {
factory.create(12345L);
throw new AssertionError("RandomGeneratorFactory.create(long) was expected" +
"to throw UnsupportedOperationException for " + factory.name() + " but didn't");
} catch (UnsupportedOperationException ignored) {
}
}
default -> coverRandomGenerator(factory.create(12345L));
}
// test create(byte[])
switch (factory.name()) {
// these don't have byte[] constructors so we expect UnsupportedOperationException
case "Random",
"SplittableRandom" -> {
try {
factory.create(new byte[] {1, 2, 3, 4, 5, 6, 7, 8});
throw new AssertionError("RandomGeneratorFactory.create(byte[]) was expected" +
"to throw UnsupportedOperationException for " + factory.name() + " but didn't");
} catch (UnsupportedOperationException ignored) {
}
}
default -> coverRandomGenerator(factory.create(new byte[] {1, 2, 3, 4, 5, 6, 7, 8}));
}
}
static void coverDefaults() {
RandomGeneratorFactory<RandomGenerator> factory =
RandomGeneratorFactory.getDefault();
RandomGenerator rng = RandomGenerator.getDefault();
}
public static void main(String[] args) throws Throwable {
RandomGeneratorFactory.all()
.forEach(factory -> {
coverFactory(factory);
coverOf(factory.name());
});
RandomGeneratorFactory.all()
.filter(RandomGeneratorFactory::isStreamable)
.forEach(factory -> {
coverStreamable((StreamableGenerator)factory.create());
});
RandomGeneratorFactory.all()
.filter(RandomGeneratorFactory::isSplittable)
.forEach(factory -> {
coverSplittable((SplittableGenerator)factory.create());
});
RandomGeneratorFactory.all()
.filter(RandomGeneratorFactory::isJumpable)
.forEach(factory -> {
coverJumpable((JumpableGenerator)factory.create());
});
RandomGeneratorFactory.all()
.filter(RandomGeneratorFactory::isLeapable)
.forEach(factory -> {
coverLeapable((LeapableGenerator)factory.create());
});
RandomGeneratorFactory.all()
.filter(RandomGeneratorFactory::isArbitrarilyJumpable)
.forEach(factory -> {
coverArbitrarilyJumpable((ArbitrarilyJumpableGenerator)factory.create());
});
RandomGeneratorFactory.all()
.forEach(RandomTestCoverage::checkPredicates);
coverRandomGenerator(new SecureRandom());
coverRandomGenerator(ThreadLocalRandom.current());
coverDefaults();
}
}

View file

@ -0,0 +1,206 @@
/*
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.ArrayList;
import java.util.List;
import java.util.random.*;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.DoubleSupplier;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
/**
* @test
* @summary test bit sequences produced by clases that implement interface RandomGenerator
* @bug 8248862
* @run main RandomTestMoments
* @author Guy Steele
* @key randomness
*/
public class RandomTestMoments {
static String currentRNG = "";
static int failCount = 0;
static void exceptionOnFail() {
if (failCount != 0) {
throw new RuntimeException(failCount + " fails detected");
}
}
static void setRNG(String rng) {
currentRNG = rng;
}
static void fail(String format, Object... args) {
if (currentRNG.length() != 0) {
System.err.println(currentRNG);
currentRNG = "";
}
System.err.format(" " + format, args);
failCount++;
}
static final int SEQUENCE_SIZE = 50000;
// Moment k of uniform distribution over [0.0,1.0) is 1.0/(1+k).
static double[][] momentsUniform = {
{ 1.00000, 1.00000, 0.01000 },
{ 0.500000, 0.500000, 0.0266265 },
{ 0.333333, 0.333333, 0.0391128 },
{ 0.250000, 0.250000, 0.0477151 },
{ 0.200000, 0.200000, 0.0540496 },
{ 0.166667, 0.166667, 0.0589355 },
{ 0.142857, 0.142857, 0.0628462 },
{ 0.125000, 0.125000, 0.0660693 },
{ 0.111111, 0.111111, 0.0688036 },
{ 0.100000, 0.100000, 0.0712002 },
{ 0.0909091, 0.0909091, 0.0733755 },
{ 0.0833333, 0.0833333, 0.0754172 },
{ 0.0769231, 0.0769231, 0.0773868 },
{ 0.0714286, 0.0714286, 0.0793244 },
{ 0.0666667, 0.0666667, 0.0812526 },
{ 0.0625000, 0.0625000, 0.0831806 },
};
// Moment k of exponential distribution with mean 1 is k!.
static double[][] momentsExponential = {
{ 1.00000, 1.00000, 0.01000 },
{ 1.00000, 1.00000, 0.0718997 },
{ 2.00000, 2.00000, 0.153241 },
{ 6.00000, 6.00000, 0.282813 },
{ 24.0000, 24.0000, 0.503707 },
};
// Absolute moment k of Gaussian distribution with mean 0 and stddev 1 is
// pow(2.0,k/2.0)*gamma((k+1)/2.0)/sqrt(PI)
// https://arxiv.org/pdf/1209.4340.pdf, equation (18)
static double[][] absoluteMomentsGaussian = {
{ 1.00000, 1.00000, 0.01 },
{ 0.797885, 0.797885, 0.1 },
{ 1.00000, 1.00000, 0.1 },
{ 1.59577, 1.59577, 0.2 },
{ 3.00000, 3.00000, 0.2 },
{ 6.38308, 6.38308, 0.2 },
{ 15.0000, 15.0000, 0.2 },
{ 38.2985, 38.2985, 0.2 },
{ 105.000, 105.000, 0.4 },
};
static void checkMoments(String test, double[] measurements, double[][] standard) {
for (int j = 0; j < measurements.length; j++) {
double actual = measurements[j];
double expected = standard[j][0];
double basis = standard[j][1];
double tolerance = standard[j][2];
if (!(Math.abs(actual - expected)/basis < tolerance)) {
fail("%s fail: actual=%f, expected=%f, basis=%f, tolerance=%f\n",
test, actual, expected, basis, tolerance);
}
}
}
static void testMomentsGaussian(DoubleSupplier theSupplier) {
int m = absoluteMomentsGaussian.length;
double[] absoluteMoments = new double[m];
for (int j = 0; j < SEQUENCE_SIZE; j++) {
double v = theSupplier.getAsDouble();
double z = 1.0;
for (int k = 0; k < m; k++) {
absoluteMoments[k] += Math.abs(z);
z *= v;
}
}
for (int k = 0; k < m; k++) {
absoluteMoments[k] /= SEQUENCE_SIZE;
}
checkMoments("Gaussian", absoluteMoments, absoluteMomentsGaussian);
}
static void testMomentsExponential(DoubleSupplier theSupplier) {
int m = momentsExponential.length;
double[] moments = new double[m];
for (int j = 0; j < SEQUENCE_SIZE; j++) {
double v = theSupplier.getAsDouble();
double z = 1.0;
for (int k = 0; k < m; k++) {
moments[k] += z;
z *= v;
}
}
for (int k = 0; k < m; k++) {
moments[k] /= SEQUENCE_SIZE;
}
checkMoments("Exponential", moments, momentsExponential);
}
static void testMomentsUniform(DoubleSupplier theSupplier) {
int m = momentsUniform.length;
double[] moments = new double[m];
for (int j = 0; j < SEQUENCE_SIZE; j++) {
double v = theSupplier.getAsDouble();
double z = 1.0;
for (int k = 0; k < m; k++) {
moments[k] += z;
z *= v;
}
}
for (int k = 0; k < m; k++) {
moments[k] /= SEQUENCE_SIZE;
}
checkMoments("Uniform", moments, momentsUniform);
}
static void testOneRng(RandomGenerator rng) {
testMomentsGaussian(rng::nextGaussian);
testMomentsExponential(rng::nextExponential);
testMomentsUniform(rng::nextDouble);
testMomentsUniform(rng.doubles().iterator()::next);
}
public static void main(String[] args) {
RandomGeneratorFactory.all()
.filter(f -> !f.name().equals("SecureRandom"))
.forEach(factory -> {
setRNG(factory.name());
testOneRng(factory.create(325) );
});
exceptionOnFail();
}
}

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.random.*;
import jdk.internal.util.random.RandomSupport;
/**
* @test
* @summary RandomSupport.convertSeedBytesToLongs sign extension overwrites previous bytes.
* @bug 8282144 8294509
* @modules java.base/jdk.internal.util.random
* @run main T8282144
* @key randomness
*/
public class T8282144 {
public static void main(String[] args) {
testLongs();
testInts();
}
private static void testLongs() {
RandomGenerator rng = RandomGeneratorFactory.of("L64X128MixRandom").create(42);
for (int i = 1; i < 8; i++) {
byte[] seed = new byte[i];
for (int j = 0; j < 10; j++) {
rng.nextBytes(seed);
long[] existing = RandomSupport.convertSeedBytesToLongs(seed, 1, 1);
long[] testing = convertSeedBytesToLongsFixed(seed, 1, 1);
for (int k = 0; k < existing.length; k++) {
if (existing[k] != testing[k]) {
throw new RuntimeException("convertSeedBytesToLongs incorrect");
}
}
}
}
}
private static void testInts() {
RandomGenerator rng = RandomGeneratorFactory.of("L64X128MixRandom").create(42);
for (int i = 1; i < 8; i++) {
byte[] seed = new byte[i];
for (int j = 0; j < 10; j++) {
rng.nextBytes(seed);
int[] existing = RandomSupport.convertSeedBytesToInts(seed, 1, 1);
int[] testing = convertSeedBytesToIntsFixed(seed, 1, 1);
for (int k = 0; k < existing.length; k++) {
if (existing[k] != testing[k]) {
throw new RuntimeException("convertSeedBytesToInts incorrect");
}
}
}
}
}
public static long[] convertSeedBytesToLongsFixed(byte[] seed, int n, int z) {
final long[] result = new long[n];
final int m = Math.min(seed.length, n << 3);
// Distribute seed bytes into the words to be formed.
for (int j = 0; j < m; j++) {
result[j >> 3] = (result[j >> 3] << 8) | (seed[j] & 0xff);
}
return result;
}
public static int[] convertSeedBytesToIntsFixed(byte[] seed, int n, int z) {
final int[] result = new int[n];
final int m = Math.min(seed.length, n << 2);
// Distribute seed bytes into the words to be formed.
for (int j = 0; j < m; j++) {
result[j >> 2] = (result[j >> 2] << 8) | (seed[j] & 0xff);
}
return result;
}
}