From 5e8ed0832d0a0e452d223ed602b67adbee5b4de8 Mon Sep 17 00:00:00 2001 From: psandoz Date: Wed, 28 Aug 2013 22:11:14 +0200 Subject: [PATCH] 8023155: Ensure functional consistency across Random, ThreadLocalRandom, SplittableRandom Reviewed-by: mduigou Contributed-by: Doug Lea , Paul Sandoz --- src/share/classes/java/util/Random.java | 700 ++++++++++++-- .../util/concurrent/ThreadLocalRandom.java | 871 +++++++++++++++--- test/java/util/Random/RandomStreamTest.java | 27 - test/java/util/Random/RandomTest.java | 430 +++++++++ .../SplittableRandomTest.java | 53 +- .../ThreadLocalRandomTest.java | 559 +++++++++++ 6 files changed, 2427 insertions(+), 213 deletions(-) create mode 100644 test/java/util/Random/RandomTest.java create mode 100644 test/java/util/concurrent/ThreadLocalRandom/ThreadLocalRandomTest.java diff --git a/src/share/classes/java/util/Random.java b/src/share/classes/java/util/Random.java index ae2551c0f9..2c0e0135c7 100644 --- a/src/share/classes/java/util/Random.java +++ b/src/share/classes/java/util/Random.java @@ -26,9 +26,13 @@ package java.util; import java.io.*; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; +import java.util.stream.StreamSupport; import sun.misc.Unsafe; @@ -85,6 +89,13 @@ class Random implements java.io.Serializable { private static final long addend = 0xBL; private static final long mask = (1L << 48) - 1; + private static final double DOUBLE_UNIT = 1.0 / (1L << 53); + + // IllegalArgumentException messages + static final String BadBound = "bound must be positive"; + static final String BadRange = "bound must be greater than origin"; + static final String BadSize = "size must be non-negative"; + /** * Creates a new random number generator. This constructor sets * the seed of the random number generator to a value very likely @@ -221,6 +232,82 @@ public void nextBytes(byte[] bytes) { bytes[i++] = (byte)rnd; } + /** + * The form of nextLong used by LongStream Spliterators. If + * origin is greater than bound, acts as unbounded form of + * nextLong, else as bounded form. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final long internalNextLong(long origin, long bound) { + long r = nextLong(); + if (origin < bound) { + long n = bound - origin, m = n - 1; + if ((n & m) == 0L) // power of two + r = (r & m) + origin; + else if (n > 0L) { // reject over-represented candidates + for (long u = r >>> 1; // ensure nonnegative + u + m - (r = u % n) < 0L; // rejection check + u = nextLong() >>> 1) // retry + ; + r += origin; + } + else { // range not representable as long + while (r < origin || r >= bound) + r = nextLong(); + } + } + return r; + } + + /** + * The form of nextInt used by IntStream Spliterators. + * For the unbounded case: uses nextInt(). + * For the bounded case with representable range: uses nextInt(int bound) + * For the bounded case with unrepresentable range: uses nextInt() + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final int internalNextInt(int origin, int bound) { + if (origin < bound) { + int n = bound - origin; + if (n > 0) { + return nextInt(n) + origin; + } + else { // range not representable as int + int r; + do { + r = nextInt(); + } while (r < origin || r >= bound); + return r; + } + } + else { + return nextInt(); + } + } + + /** + * The form of nextDouble used by DoubleStream Spliterators. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final double internalNextDouble(double origin, double bound) { + double r = nextDouble(); + if (origin < bound) { + r = r * (bound - origin) + origin; + if (r >= bound) // correct for rounding + r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + } + return r; + } + /** * Returns the next pseudorandom, uniformly distributed {@code int} * value from this random number generator's sequence. The general @@ -247,23 +334,23 @@ public int nextInt() { * between 0 (inclusive) and the specified value (exclusive), drawn from * this random number generator's sequence. The general contract of * {@code nextInt} is that one {@code int} value in the specified range - * is pseudorandomly generated and returned. All {@code n} possible + * is pseudorandomly generated and returned. All {@code bound} possible * {@code int} values are produced with (approximately) equal - * probability. The method {@code nextInt(int n)} is implemented by + * probability. The method {@code nextInt(int bound)} is implemented by * class {@code Random} as if by: *
 {@code
-     * public int nextInt(int n) {
-     *   if (n <= 0)
-     *     throw new IllegalArgumentException("n must be positive");
+     * public int nextInt(int bound) {
+     *   if (bound <= 0)
+     *     throw new IllegalArgumentException("bound must be positive");
      *
-     *   if ((n & -n) == n)  // i.e., n is a power of 2
-     *     return (int)((n * (long)next(31)) >> 31);
+     *   if ((bound & -bound) == bound)  // i.e., bound is a power of 2
+     *     return (int)((bound * (long)next(31)) >> 31);
      *
      *   int bits, val;
      *   do {
      *       bits = next(31);
-     *       val = bits % n;
-     *   } while (bits - val + (n-1) < 0);
+     *       val = bits % bound;
+     *   } while (bits - val + (bound-1) < 0);
      *   return val;
      * }}
* @@ -289,28 +376,28 @@ public int nextInt() { * greatly increases the length of the sequence of values returned by * successive calls to this method if n is a small power of two. * - * @param n the bound on the random number to be returned. Must be - * positive. + * @param bound the upper bound (exclusive). Must be positive. * @return the next pseudorandom, uniformly distributed {@code int} - * value between {@code 0} (inclusive) and {@code n} (exclusive) + * value between zero (inclusive) and {@code bound} (exclusive) * from this random number generator's sequence - * @throws IllegalArgumentException if n is not positive + * @throws IllegalArgumentException if bound is not positive * @since 1.2 */ + public int nextInt(int bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); - public int nextInt(int n) { - if (n <= 0) - throw new IllegalArgumentException("n must be positive"); - - if ((n & -n) == n) // i.e., n is a power of 2 - return (int)((n * (long)next(31)) >> 31); - - int bits, val; - do { - bits = next(31); - val = bits % n; - } while (bits - val + (n-1) < 0); - return val; + int r = next(31); + int m = bound - 1; + if ((bound & m) == 0) // i.e., bound is a power of 2 + r = (int)((bound * (long)r) >> 31); + else { + for (int u = r; + u - (r = u % bound) + m < 0; + u = next(31)) + ; + } + return r; } /** @@ -442,8 +529,7 @@ public float nextFloat() { * @see Math#random */ public double nextDouble() { - return (((long)(next(26)) << 27) + next(27)) - / (double)(1L << 53); + return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT; } private double nextNextGaussian; @@ -513,57 +599,563 @@ synchronized public double nextGaussian() { } } + // stream methods, coded in a way intended to better isolate for + // maintenance purposes the small differences across forms. + /** - * Returns a stream of pseudorandom, uniformly distributed - * {@code integer} values from this random number generator's - * sequence. Values are obtained as needed by calling - * {@link #nextInt()}. + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code int} values. * - * @return an infinite stream of {@code integer} values + *

A pseudorandom {@code int} value is generated as if it's the result of + * calling the method {@link #nextInt()}. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code int} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @since 1.8 + */ + public IntStream ints(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, streamSize, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code int} + * values. + * + *

A pseudorandom {@code int} value is generated as if it's the result of + * calling the method {@link #nextInt()}. + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code int} values * @since 1.8 */ public IntStream ints() { - return IntStream.generate(this::nextInt); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number + * of pseudorandom {@code int} values, each conforming to the given + * origin (inclusive) and bound (exclusive). + * + *

A pseudorandom {@code int} value is generated as if it's the result of + * calling the following method with the origin and bound: + *

 {@code
+     * int nextInt(int origin, int bound) {
+     *   int n = bound - origin;
+     *   if (n > 0) {
+     *     return nextInt(n) + origin;
+     *   }
+     *   else {  // range not representable as int
+     *     int r;
+     *     do {
+     *       r = nextInt();
+     *     } while (r < origin || r >= bound);
+     *     return r;
+     *   }
+     * }}
+ * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public IntStream ints(long streamSize, int randomNumberOrigin, + int randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); } /** - * Returns a stream of pseudorandom, uniformly distributed - * {@code long} values from this random number generator's - * sequence. Values are obtained as needed by calling - * {@link #nextLong()}. + * Returns an effectively unlimited stream of pseudorandom {@code + * int} values, each conforming to the given origin (inclusive) and bound + * (exclusive). + * + *

A pseudorandom {@code int} value is generated as if it's the result of + * calling the following method with the origin and bound: + *

 {@code
+     * int nextInt(int origin, int bound) {
+     *   int n = bound - origin;
+     *   if (n > 0) {
+     *     return nextInt(n) + origin;
+     *   }
+     *   else {  // range not representable as int
+     *     int r;
+     *     do {
+     *       r = nextInt();
+     *     } while (r < origin || r >= bound);
+     *     return r;
+     *   }
+     * }}
+ * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. * - * @return an infinite stream of {@code long} values + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public IntStream ints(int randomNumberOrigin, int randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long} values. + * + *

A pseudorandom {@code long} value is generated as if it's the result + * of calling the method {@link #nextLong()}. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code long} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @since 1.8 + */ + public LongStream longs(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, streamSize, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code long} + * values. + * + *

A pseudorandom {@code long} value is generated as if it's the result + * of calling the method {@link #nextLong()}. + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code long} values * @since 1.8 */ public LongStream longs() { - return LongStream.generate(this::nextLong); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long}, each conforming to the given origin + * (inclusive) and bound (exclusive). + * + *

A pseudorandom {@code long} value is generated as if it's the result + * of calling the following method with the origin and bound: + *

 {@code
+     * long nextLong(long origin, long bound) {
+     *   long r = nextLong();
+     *   long n = bound - origin, m = n - 1;
+     *   if ((n & m) == 0L)  // power of two
+     *     r = (r & m) + origin;
+     *   else if (n > 0L) {  // reject over-represented candidates
+     *     for (long u = r >>> 1;            // ensure nonnegative
+     *          u + m - (r = u % n) < 0L;    // rejection check
+     *          u = nextLong() >>> 1) // retry
+     *         ;
+     *     r += origin;
+     *   }
+     *   else {              // range not representable as long
+     *     while (r < origin || r >= bound)
+     *       r = nextLong();
+     *   }
+     *   return r;
+     * }}
+ * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public LongStream longs(long streamSize, long randomNumberOrigin, + long randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * long} values, each conforming to the given origin (inclusive) and bound + * (exclusive). + * + *

A pseudorandom {@code long} value is generated as if it's the result + * of calling the following method with the origin and bound: + *

 {@code
+     * long nextLong(long origin, long bound) {
+     *   long r = nextLong();
+     *   long n = bound - origin, m = n - 1;
+     *   if ((n & m) == 0L)  // power of two
+     *     r = (r & m) + origin;
+     *   else if (n > 0L) {  // reject over-represented candidates
+     *     for (long u = r >>> 1;            // ensure nonnegative
+     *          u + m - (r = u % n) < 0L;    // rejection check
+     *          u = nextLong() >>> 1) // retry
+     *         ;
+     *     r += origin;
+     *   }
+     *   else {              // range not representable as long
+     *     while (r < origin || r >= bound)
+     *       r = nextLong();
+     *   }
+     *   return r;
+     * }}
+ * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public LongStream longs(long randomNumberOrigin, long randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); } /** - * Returns a stream of pseudorandom, uniformly distributed - * {@code double} values between {@code 0.0} and {@code 1.0} - * from this random number generator's sequence. Values are - * obtained as needed by calling {@link #nextDouble()}. + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each between zero + * (inclusive) and one (exclusive). + * + *

A pseudorandom {@code double} value is generated as if it's the result + * of calling the method {@link #nextDouble()}}. * - * @return an infinite stream of {@code double} values + * @param streamSize the number of values to generate + * @return a stream of {@code double} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @since 1.8 + */ + public DoubleStream doubles(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, streamSize, Double.MAX_VALUE, 0.0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each between zero (inclusive) and one + * (exclusive). + * + *

A pseudorandom {@code double} value is generated as if it's the result + * of calling the method {@link #nextDouble()}}. + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code double} values * @since 1.8 */ public DoubleStream doubles() { - return DoubleStream.generate(this::nextDouble); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0), + false); } /** - * Returns a stream of pseudorandom, Gaussian ("normally") - * distributed {@code double} values with mean {@code 0.0} - * and standard deviation {@code 1.0} from this random number - * generator's sequence. Values are obtained as needed by - * calling {@link #nextGaussian()}. + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each conforming to the given origin + * (inclusive) and bound (exclusive). + * + *

A pseudorandom {@code double} value is generated as if it's the result + * of calling the following method with the origin and bound: + *

 {@code
+     * double nextDouble(double origin, double bound) {
+     *   double r = nextDouble();
+     *   r = r * (bound - origin) + origin;
+     *   if (r >= bound) // correct for rounding
+     *     r = Math.nextDown(bound);
+     *   return r;
+     * }}
* - * @return an infinite stream of {@code double} values + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} * @since 1.8 */ - public DoubleStream gaussians() { - return DoubleStream.generate(this::nextGaussian); + public DoubleStream doubles(long streamSize, double randomNumberOrigin, + double randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each conforming to the given origin (inclusive) and bound + * (exclusive). + * + *

A pseudorandom {@code double} value is generated as if it's the result + * of calling the following method with the origin and bound: + *

 {@code
+     * double nextDouble(double origin, double bound) {
+     *   double r = nextDouble();
+     *   r = r * (bound - origin) + origin;
+     *   if (r >= bound) // correct for rounding
+     *     r = Math.nextDown(bound);
+     *   return r;
+     * }}
+ * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) { + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Spliterator for int streams. We multiplex the four int + * versions into one class by treating a bound less than origin as + * unbounded, and also by treating "infinite" as equivalent to + * Long.MAX_VALUE. For splits, it uses the standard divide-by-two + * approach. The long and double versions of this class are + * identical except for types. + */ + static final class RandomIntsSpliterator implements Spliterator.OfInt { + final Random rng; + long index; + final long fence; + final int origin; + final int bound; + RandomIntsSpliterator(Random rng, long index, long fence, + int origin, int bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomIntsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomIntsSpliterator(rng, i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextInt(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + Random r = rng; + int o = origin, b = bound; + do { + consumer.accept(r.internalNextInt(o, b)); + } while (++i < f); + } + } + } + + /** + * Spliterator for long streams. + */ + static final class RandomLongsSpliterator implements Spliterator.OfLong { + final Random rng; + long index; + final long fence; + final long origin; + final long bound; + RandomLongsSpliterator(Random rng, long index, long fence, + long origin, long bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomLongsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomLongsSpliterator(rng, i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextLong(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + Random r = rng; + long o = origin, b = bound; + do { + consumer.accept(r.internalNextLong(o, b)); + } while (++i < f); + } + } + + } + + /** + * Spliterator for double streams. + */ + static final class RandomDoublesSpliterator implements Spliterator.OfDouble { + final Random rng; + long index; + final long fence; + final double origin; + final double bound; + RandomDoublesSpliterator(Random rng, long index, long fence, + double origin, double bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomDoublesSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomDoublesSpliterator(rng, i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextDouble(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + Random r = rng; + double o = origin, b = bound; + do { + consumer.accept(r.internalNextDouble(o, b)); + } while (++i < f); + } + } } /** diff --git a/src/share/classes/java/util/concurrent/ThreadLocalRandom.java b/src/share/classes/java/util/concurrent/ThreadLocalRandom.java index 0532d3d438..297f88cd5a 100644 --- a/src/share/classes/java/util/concurrent/ThreadLocalRandom.java +++ b/src/share/classes/java/util/concurrent/ThreadLocalRandom.java @@ -37,11 +37,16 @@ import java.io.ObjectStreamField; import java.util.Random; +import java.util.Spliterator; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; +import java.util.stream.StreamSupport; /** * A random number generator isolated to the current thread. Like the @@ -64,6 +69,10 @@ *

This class also provides additional commonly used bounded random * generation methods. * + *

Instances of {@code ThreadLocalRandom} are not cryptographically + * secure. Consider instead using {@link java.security.SecureRandom} + * in security-sensitive applications. + * * @since 1.7 * @author Doug Lea */ @@ -85,28 +94,26 @@ public class ThreadLocalRandom extends Random { * application-level overhead and footprint of most concurrent * programs. * + * Even though this class subclasses java.util.Random, it uses the + * same basic algorithm as java.util.SplittableRandom. (See its + * internal documentation for explanations, which are not repeated + * here.) Because ThreadLocalRandoms are not splittable + * though, we use only a single 64bit gamma. + * * Because this class is in a different package than class Thread, * field access methods use Unsafe to bypass access control rules. - * The base functionality of Random methods is conveniently - * isolated in method next(bits), that just reads and writes the - * Thread field rather than its own field. However, to conform to - * the requirements of the Random superclass constructor, the - * common static ThreadLocalRandom maintains an "initialized" - * field for the sake of rejecting user calls to setSeed while - * still allowing a call from constructor. Note that - * serialization is completely unnecessary because there is only a - * static singleton. But we generate a serial form containing - * "rnd" and "initialized" fields to ensure compatibility across - * versions. - * - * Per-thread initialization is similar to that in the no-arg - * Random constructor, but we avoid correlation among not only - * initial seeds of those created in different threads, but also - * those created using class Random itself; while at the same time - * not changing any statistical properties. So we use the same - * underlying multiplicative sequence, but start the sequence far - * away from the base version, and then merge (xor) current time - * and per-thread probe bits to generate initial values. + * To conform to the requirements of the Random superclass + * constructor, the common static ThreadLocalRandom maintains an + * "initialized" field for the sake of rejecting user calls to + * setSeed while still allowing a call from constructor. Note + * that serialization is completely unnecessary because there is + * only a static singleton. But we generate a serial form + * containing "rnd" and "initialized" fields to ensure + * compatibility across versions. + * + * Implementations of non-core methods are mostly the same as in + * SplittableRandom, that were in part derived from a previous + * version of this class. * * The nextLocalGaussian ThreadLocal supports the very rarely used * nextGaussian method by providing a holder for the second of a @@ -115,24 +122,51 @@ public class ThreadLocalRandom extends Random { * but we provide identical statistical properties. */ - // same constants as Random, but must be redeclared because private - private static final long multiplier = 0x5DEECE66DL; - private static final long addend = 0xBL; - private static final long mask = (1L << 48) - 1; - private static final int PROBE_INCREMENT = 0x61c88647; - - /** Generates the basis for per-thread initial seed values */ - private static final AtomicLong seedGenerator = - new AtomicLong(1269533684904616924L); - /** Generates per-thread initialization/probe field */ private static final AtomicInteger probeGenerator = - new AtomicInteger(0xe80f8647); + new AtomicInteger(); + + /** + * The next seed for default constructors. + */ + private static final AtomicLong seeder = + new AtomicLong(mix64(System.currentTimeMillis()) ^ + mix64(System.nanoTime())); + + /** + * The seed increment + */ + private static final long GAMMA = 0x9e3779b97f4a7c15L; + + /** + * The increment for generating probe values + */ + private static final int PROBE_INCREMENT = 0x9e3779b9; + + /** + * The increment of seeder per new instance + */ + private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL; + + // Constants from SplittableRandom + private static final double DOUBLE_UNIT = 1.0 / (1L << 53); + private static final float FLOAT_UNIT = 1.0f / (1 << 24); /** Rarely-used holder for the second of a pair of Gaussians */ private static final ThreadLocal nextLocalGaussian = new ThreadLocal(); + private static long mix64(long z) { + z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; + z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L; + return z ^ (z >>> 33); + } + + private static int mix32(long z) { + z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; + return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32); + } + /** * Field used only during singleton initialization. * True when constructor completes. @@ -155,16 +189,11 @@ private ThreadLocalRandom() { * rely on (static) atomic generators to initialize the values. */ static final void localInit() { - int p = probeGenerator.getAndAdd(PROBE_INCREMENT); + int p = probeGenerator.addAndGet(PROBE_INCREMENT); int probe = (p == 0) ? 1 : p; // skip 0 - long current, next; - do { // same sequence as j.u.Random but different initial value - current = seedGenerator.get(); - next = current * 181783497276652981L; - } while (!seedGenerator.compareAndSet(current, next)); - long r = next ^ ((long)probe << 32) ^ System.nanoTime(); + long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT)); Thread t = Thread.currentThread(); - UNSAFE.putLong(t, SEED, r); + UNSAFE.putLong(t, SEED, seed); UNSAFE.putInt(t, PROBE, probe); } @@ -191,124 +220,264 @@ public void setSeed(long seed) { throw new UnsupportedOperationException(); } - protected int next(int bits) { + final long nextSeed() { Thread t; long r; // read and update per-thread seed - UNSAFE.putLong - (t = Thread.currentThread(), SEED, - r = (UNSAFE.getLong(t, SEED) * multiplier + addend) & mask); - return (int) (r >>> (48-bits)); + UNSAFE.putLong(t = Thread.currentThread(), SEED, + r = UNSAFE.getLong(t, SEED) + GAMMA); + return r; } + // We must define this, but never use it. + protected int next(int bits) { + return (int)(mix64(nextSeed()) >>> (64 - bits)); + } + + // IllegalArgumentException messages + static final String BadBound = "bound must be positive"; + static final String BadRange = "bound must be greater than origin"; + static final String BadSize = "size must be non-negative"; + /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). + * The form of nextLong used by LongStream Spliterators. If + * origin is greater than bound, acts as unbounded form of + * nextLong, else as bounded form. * - * @param least the least value returned - * @param bound the upper bound (exclusive) - * @throws IllegalArgumentException if least greater than or equal - * to bound - * @return the next value - */ - public int nextInt(int least, int bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextInt(bound - least) + least; - } - - /** - * Returns a pseudorandom, uniformly distributed value - * between 0 (inclusive) and the specified value (exclusive). - * - * @param n the bound on the random number to be returned. Must be - * positive. - * @return the next value - * @throws IllegalArgumentException if n is not positive - */ - public long nextLong(long n) { - if (n <= 0) - throw new IllegalArgumentException("n must be positive"); - // Divide n by two until small enough for nextInt. On each - // iteration (at most 31 of them but usually much less), - // randomly choose both whether to include high bit in result - // (offset) and whether to continue with the lower vs upper - // half (which makes a difference only if odd). - long offset = 0; - while (n >= Integer.MAX_VALUE) { - int bits = next(2); - long half = n >>> 1; - long nextn = ((bits & 2) == 0) ? half : n - half; - if ((bits & 1) == 0) - offset += n - nextn; - n = nextn; + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final long internalNextLong(long origin, long bound) { + long r = mix64(nextSeed()); + if (origin < bound) { + long n = bound - origin, m = n - 1; + if ((n & m) == 0L) // power of two + r = (r & m) + origin; + else if (n > 0L) { // reject over-represented candidates + for (long u = r >>> 1; // ensure nonnegative + u + m - (r = u % n) < 0L; // rejection check + u = mix64(nextSeed()) >>> 1) // retry + ; + r += origin; + } + else { // range not representable as long + while (r < origin || r >= bound) + r = mix64(nextSeed()); + } } - return offset + nextInt((int) n); + return r; } - @Override - public IntStream ints() { - return IntStream.generate(() -> current().nextInt()); + /** + * The form of nextInt used by IntStream Spliterators. + * Exactly the same as long version, except for types. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final int internalNextInt(int origin, int bound) { + int r = mix32(nextSeed()); + if (origin < bound) { + int n = bound - origin, m = n - 1; + if ((n & m) == 0) + r = (r & m) + origin; + else if (n > 0) { + for (int u = r >>> 1; + u + m - (r = u % n) < 0; + u = mix32(nextSeed()) >>> 1) + ; + r += origin; + } + else { + while (r < origin || r >= bound) + r = mix32(nextSeed()); + } + } + return r; } - @Override - public LongStream longs() { - return LongStream.generate(() -> current().nextLong()); + /** + * The form of nextDouble used by DoubleStream Spliterators. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final double internalNextDouble(double origin, double bound) { + double r = (nextLong() >>> 11) * DOUBLE_UNIT; + if (origin < bound) { + r = r * (bound - origin) + origin; + if (r >= bound) // correct for rounding + r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + } + return r; } - @Override - public DoubleStream doubles() { - return DoubleStream.generate(() -> current().nextDouble()); + /** + * Returns a pseudorandom {@code int} value. + * + * @return a pseudorandom {@code int} value + */ + public int nextInt() { + return mix32(nextSeed()); } - @Override - public DoubleStream gaussians() { - return DoubleStream.generate(() -> current().nextGaussian()); + /** + * Returns a pseudorandom {@code int} value between zero (inclusive) + * and the specified bound (exclusive). + * + * @param bound the upper bound (exclusive). Must be positive. + * @return a pseudorandom {@code int} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public int nextInt(int bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); + int r = mix32(nextSeed()); + int m = bound - 1; + if ((bound & m) == 0) // power of two + r &= m; + else { // reject over-represented candidates + for (int u = r >>> 1; + u + m - (r = u % bound) < 0; + u = mix32(nextSeed()) >>> 1) + ; + } + return r; } /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). + * Returns a pseudorandom {@code int} value between the specified + * origin (inclusive) and the specified bound (exclusive). * - * @param least the least value returned + * @param origin the least value returned * @param bound the upper bound (exclusive) - * @return the next value - * @throws IllegalArgumentException if least greater than or equal - * to bound + * @return a pseudorandom {@code int} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} */ - public long nextLong(long least, long bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextLong(bound - least) + least; + public int nextInt(int origin, int bound) { + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextInt(origin, bound); } /** - * Returns a pseudorandom, uniformly distributed {@code double} value - * between 0 (inclusive) and the specified value (exclusive). + * Returns a pseudorandom {@code long} value. * - * @param n the bound on the random number to be returned. Must be - * positive. - * @return the next value - * @throws IllegalArgumentException if n is not positive + * @return a pseudorandom {@code long} value */ - public double nextDouble(double n) { - if (n <= 0) - throw new IllegalArgumentException("n must be positive"); - return nextDouble() * n; + public long nextLong() { + return mix64(nextSeed()); } /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). + * Returns a pseudorandom {@code long} value between zero (inclusive) + * and the specified bound (exclusive). * - * @param least the least value returned + * @param bound the upper bound (exclusive). Must be positive. + * @return a pseudorandom {@code long} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public long nextLong(long bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); + long r = mix64(nextSeed()); + long m = bound - 1; + if ((bound & m) == 0L) // power of two + r &= m; + else { // reject over-represented candidates + for (long u = r >>> 1; + u + m - (r = u % bound) < 0L; + u = mix64(nextSeed()) >>> 1) + ; + } + return r; + } + + /** + * Returns a pseudorandom {@code long} value between the specified + * origin (inclusive) and the specified bound (exclusive). + * + * @param origin the least value returned * @param bound the upper bound (exclusive) - * @return the next value - * @throws IllegalArgumentException if least greater than or equal - * to bound + * @return a pseudorandom {@code long} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} + */ + public long nextLong(long origin, long bound) { + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextLong(origin, bound); + } + + /** + * Returns a pseudorandom {@code double} value between zero + * (inclusive) and one (exclusive). + * + * @return a pseudorandom {@code double} value between zero + * (inclusive) and one (exclusive) + */ + public double nextDouble() { + return (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT; + } + + /** + * Returns a pseudorandom {@code double} value between 0.0 + * (inclusive) and the specified bound (exclusive). + * + * @param bound the upper bound (exclusive). Must be positive. + * @return a pseudorandom {@code double} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public double nextDouble(double bound) { + if (!(bound > 0.0)) + throw new IllegalArgumentException(BadBound); + double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound; + return (result < bound) ? result : // correct for rounding + Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + } + + /** + * Returns a pseudorandom {@code double} value between the specified + * origin (inclusive) and bound (exclusive). + * + * @param origin the least value returned + * @param bound the upper bound (exclusive) + * @return a pseudorandom {@code double} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} + */ + public double nextDouble(double origin, double bound) { + if (!(origin < bound)) + throw new IllegalArgumentException(BadRange); + return internalNextDouble(origin, bound); + } + + /** + * Returns a pseudorandom {@code boolean} value. + * + * @return a pseudorandom {@code boolean} value */ - public double nextDouble(double least, double bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextDouble() * (bound - least) + least; + public boolean nextBoolean() { + return mix32(nextSeed()) < 0; + } + + /** + * Returns a pseudorandom {@code float} value between zero + * (inclusive) and one (exclusive). + * + * @return a pseudorandom {@code float} value between zero + * (inclusive) and one (exclusive) + */ + public float nextFloat() { + return (mix32(nextSeed()) >>> 8) * FLOAT_UNIT; } public double nextGaussian() { @@ -329,6 +498,445 @@ public double nextGaussian() { return v1 * multiplier; } + // stream methods, coded in a way intended to better isolate for + // maintenance purposes the small differences across forms. + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code int} values. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code int} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @since 1.8 + */ + public IntStream ints(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.intStream + (new RandomIntsSpliterator + (0L, streamSize, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code int} + * values. + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code int} values + * @since 1.8 + */ + public IntStream ints() { + return StreamSupport.intStream + (new RandomIntsSpliterator + (0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number + * of pseudorandom {@code int} values, each conforming to the given + * origin (inclusive) and bound (exclusive). + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public IntStream ints(long streamSize, int randomNumberOrigin, + int randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * int} values, each conforming to the given origin (inclusive) and bound + * (exclusive). + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public IntStream ints(int randomNumberOrigin, int randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long} values. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code long} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @since 1.8 + */ + public LongStream longs(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.longStream + (new RandomLongsSpliterator + (0L, streamSize, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code long} + * values. + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code long} values + * @since 1.8 + */ + public LongStream longs() { + return StreamSupport.longStream + (new RandomLongsSpliterator + (0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long}, each conforming to the given origin + * (inclusive) and bound (exclusive). + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public LongStream longs(long streamSize, long randomNumberOrigin, + long randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * long} values, each conforming to the given origin (inclusive) and bound + * (exclusive). + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public LongStream longs(long randomNumberOrigin, long randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each between zero + * (inclusive) and one (exclusive). + * + * @param streamSize the number of values to generate + * @return a stream of {@code double} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @since 1.8 + */ + public DoubleStream doubles(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (0L, streamSize, Double.MAX_VALUE, 0.0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each between zero (inclusive) and one + * (exclusive). + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code double} values + * @since 1.8 + */ + public DoubleStream doubles() { + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each conforming to the given origin + * (inclusive) and bound (exclusive). + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public DoubleStream doubles(long streamSize, double randomNumberOrigin, + double randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each conforming to the given origin (inclusive) and bound + * (exclusive). + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin (inclusive) of each random value + * @param randomNumberBound the bound (exclusive) of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin (inclusive) and bound (exclusive) + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + * @since 1.8 + */ + public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) { + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Spliterator for int streams. We multiplex the four int + * versions into one class by treating a bound less than origin as + * unbounded, and also by treating "infinite" as equivalent to + * Long.MAX_VALUE. For splits, it uses the standard divide-by-two + * approach. The long and double versions of this class are + * identical except for types. + */ + static final class RandomIntsSpliterator implements Spliterator.OfInt { + long index; + final long fence; + final int origin; + final int bound; + RandomIntsSpliterator(long index, long fence, + int origin, int bound) { + this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomIntsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomIntsSpliterator(i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(ThreadLocalRandom.current().internalNextInt(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + int o = origin, b = bound; + ThreadLocalRandom rng = ThreadLocalRandom.current(); + do { + consumer.accept(rng.internalNextInt(o, b)); + } while (++i < f); + } + } + } + + /** + * Spliterator for long streams. + */ + static final class RandomLongsSpliterator implements Spliterator.OfLong { + long index; + final long fence; + final long origin; + final long bound; + RandomLongsSpliterator(long index, long fence, + long origin, long bound) { + this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomLongsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomLongsSpliterator(i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(ThreadLocalRandom.current().internalNextLong(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + long o = origin, b = bound; + ThreadLocalRandom rng = ThreadLocalRandom.current(); + do { + consumer.accept(rng.internalNextLong(o, b)); + } while (++i < f); + } + } + + } + + /** + * Spliterator for double streams. + */ + static final class RandomDoublesSpliterator implements Spliterator.OfDouble { + long index; + final long fence; + final double origin; + final double bound; + RandomDoublesSpliterator(long index, long fence, + double origin, double bound) { + this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomDoublesSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomDoublesSpliterator(i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(ThreadLocalRandom.current().internalNextDouble(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + double o = origin, b = bound; + ThreadLocalRandom rng = ThreadLocalRandom.current(); + do { + consumer.accept(rng.internalNextDouble(o, b)); + } while (++i < f); + } + } + } + + // Within-package utilities /* @@ -401,23 +1009,26 @@ static final int nextSecondarySeed() { */ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("rnd", long.class), - new ObjectStreamField("initialized", boolean.class) + new ObjectStreamField("initialized", boolean.class), }; /** * Saves the {@code ThreadLocalRandom} to a stream (that is, serializes it). + * @param s the stream + * @throws java.io.IOException if an I/O error occurs */ - private void writeObject(java.io.ObjectOutputStream out) + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { - java.io.ObjectOutputStream.PutField fields = out.putFields(); + java.io.ObjectOutputStream.PutField fields = s.putFields(); fields.put("rnd", UNSAFE.getLong(Thread.currentThread(), SEED)); fields.put("initialized", true); - out.writeFields(); + s.writeFields(); } /** * Returns the {@link #current() current} thread's {@code ThreadLocalRandom}. + * @return the {@link #current() current} thread's {@code ThreadLocalRandom} */ private Object readResolve() { return current(); diff --git a/test/java/util/Random/RandomStreamTest.java b/test/java/util/Random/RandomStreamTest.java index f7b799c484..e96d57e263 100644 --- a/test/java/util/Random/RandomStreamTest.java +++ b/test/java/util/Random/RandomStreamTest.java @@ -82,13 +82,6 @@ public void testRandomDoubleStream(final Random random, final int count) { assertEquals(destination.size(), count); } - @Test(dataProvider = "suppliers") - public void testRandomGaussianStream(final Random random, final int count) { - final List destination = new ArrayList<>(count); - random.gaussians().limit(count).forEach(destination::add); - assertEquals(destination.size(), count); - } - @Test public void testIntStream() { final long seed = System.currentTimeMillis(); @@ -131,20 +124,6 @@ public void testDoubleStream() { assertEquals(a, b); } - @Test - public void testGaussianStream() { - 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.nextGaussian(); - } - - final Random r2 = new Random(seed); // same seed - final double[] b = r2.gaussians().limit(SIZE).toArray(); - assertEquals(a, b); - } - @Test public void testThreadLocalIntStream() throws InterruptedException, ExecutionException, TimeoutException { ThreadLocalRandom tlr = ThreadLocalRandom.current(); @@ -163,12 +142,6 @@ public void testThreadLocalDoubleStream() throws InterruptedException, Execution testRandomResultSupplierConcurrently(() -> tlr.doubles().limit(SIZE).boxed().collect(toList())); } - @Test - public void testThreadLocalGaussianStream() throws InterruptedException, ExecutionException, TimeoutException { - ThreadLocalRandom tlr = ThreadLocalRandom.current(); - testRandomResultSupplierConcurrently(() -> tlr.gaussians().limit(SIZE).boxed().collect(toList())); - } - void testRandomResultSupplierConcurrently(Supplier s) throws InterruptedException, ExecutionException, TimeoutException { // Produce 10 completable future tasks final int tasks = 10; diff --git a/test/java/util/Random/RandomTest.java b/test/java/util/Random/RandomTest.java new file mode 100644 index 0000000000..e56ffde196 --- /dev/null +++ b/test/java/util/Random/RandomTest.java @@ -0,0 +1,430 @@ +/* + * 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.Assert; +import org.testng.annotations.Test; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.BiConsumer; + +import static org.testng.Assert.*; + +/** + * @test + * @run testng RandomTest + * @summary test methods on Random + */ +@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(); + executeAndCatchIAE(() -> r.ints(-1L)); + executeAndCatchIAE(() -> r.ints(-1L, 2, 3)); + executeAndCatchIAE(() -> r.longs(-1L)); + executeAndCatchIAE(() -> r.longs(-1L, -1L, 1L)); + executeAndCatchIAE(() -> r.doubles(-1L)); + executeAndCatchIAE(() -> r.doubles(-1L, .5, .6)); + } + + /** + * Invoking bounded ints, long, doubles, with illegal bounds throws + * IllegalArgumentException + */ + public void testBadStreamBounds() { + Random r = new Random(); + executeAndCatchIAE(() -> r.ints(2, 1)); + executeAndCatchIAE(() -> r.ints(10, 42, 42)); + executeAndCatchIAE(() -> r.longs(-1L, -1L)); + executeAndCatchIAE(() -> 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 bi) { + executeAndCatchIAE(() -> bi.accept(17.0, 2.0)); + executeAndCatchIAE(() -> bi.accept(0.0, 0.0)); + executeAndCatchIAE(() -> bi.accept(Double.NaN, FINITE)); + executeAndCatchIAE(() -> bi.accept(FINITE, Double.NaN)); + executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); + + // Returns NaN +// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, FINITE)); +// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + + executeAndCatchIAE(() -> bi.accept(FINITE, Double.NEGATIVE_INFINITY)); + + // Returns Double.MAX_VALUE +// executeAndCatchIAE(() -> bi.accept(FINITE, Double.POSITIVE_INFINITY)); + + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)); + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, FINITE)); + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)); + } + + private void executeAndCatchIAE(Runnable r) { + executeAndCatch(IllegalArgumentException.class, r); + } + + private void executeAndCatch(Class expected, Runnable r) { + Exception caught = null; + try { + r.run(); + } + catch (Exception e) { + caught = e; + } + + assertNotNull(caught, + String.format("No Exception was thrown, expected an Exception of %s to be thrown", + expected.getName())); + Assert.assertTrue(expected.isInstance(caught), + String.format("Exception thrown %s not an instance of %s", + caught.getClass().getName(), expected.getName())); + } + + /** + * 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); + } + +} diff --git a/test/java/util/SplittableRandom/SplittableRandomTest.java b/test/java/util/SplittableRandom/SplittableRandomTest.java index 23ed242d06..57e1a3434a 100644 --- a/test/java/util/SplittableRandom/SplittableRandomTest.java +++ b/test/java/util/SplittableRandom/SplittableRandomTest.java @@ -25,8 +25,10 @@ import org.testng.annotations.Test; import java.util.SplittableRandom; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.LongAdder; +import java.util.function.BiConsumer; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -272,6 +274,53 @@ public void testNextLongBounded2() { } } + /** + * nextDouble(bound) throws IllegalArgumentException + */ + public void testNextDoubleBadBound() { + SplittableRandom sr = new SplittableRandom(); + executeAndCatchIAE(() -> sr.nextDouble(0.0)); + executeAndCatchIAE(() -> sr.nextDouble(-0.0)); + executeAndCatchIAE(() -> sr.nextDouble(+0.0)); + executeAndCatchIAE(() -> sr.nextDouble(-1.0)); + executeAndCatchIAE(() -> sr.nextDouble(Double.NaN)); + executeAndCatchIAE(() -> sr.nextDouble(Double.NEGATIVE_INFINITY)); + + // Returns Double.MAX_VALUE +// executeAndCatchIAE(() -> r.nextDouble(Double.POSITIVE_INFINITY)); + } + + /** + * nextDouble(origin, bound) throws IllegalArgumentException + */ + public void testNextDoubleBadOriginBound() { + testDoubleBadOriginBound(new SplittableRandom()::nextDouble); + } + + // An arbitrary finite double value + static final double FINITE = Math.PI; + + void testDoubleBadOriginBound(BiConsumer bi) { + executeAndCatchIAE(() -> bi.accept(17.0, 2.0)); + executeAndCatchIAE(() -> bi.accept(0.0, 0.0)); + executeAndCatchIAE(() -> bi.accept(Double.NaN, FINITE)); + executeAndCatchIAE(() -> bi.accept(FINITE, Double.NaN)); + executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); + + // Returns NaN +// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, FINITE)); +// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + + executeAndCatchIAE(() -> bi.accept(FINITE, Double.NEGATIVE_INFINITY)); + + // Returns Double.MAX_VALUE +// executeAndCatchIAE(() -> bi.accept(FINITE, Double.POSITIVE_INFINITY)); + + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)); + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, FINITE)); + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)); + } + /** * nextDouble(least, bound) returns least <= value < bound; * repeated calls produce at least two distinct results @@ -318,8 +367,8 @@ public void testBadStreamBounds() { executeAndCatchIAE(() -> r.ints(10, 42, 42)); executeAndCatchIAE(() -> r.longs(-1L, -1L)); executeAndCatchIAE(() -> r.longs(10, 1L, -2L)); - executeAndCatchIAE(() -> r.doubles(0.0, 0.0)); - executeAndCatchIAE(() -> r.doubles(10, .5, .4)); + + testDoubleBadOriginBound((o, b) -> r.doubles(10, o, b)); } private void executeAndCatchIAE(Runnable r) { diff --git a/test/java/util/concurrent/ThreadLocalRandom/ThreadLocalRandomTest.java b/test/java/util/concurrent/ThreadLocalRandom/ThreadLocalRandomTest.java new file mode 100644 index 0000000000..e4e91f597b --- /dev/null +++ b/test/java/util/concurrent/ThreadLocalRandom/ThreadLocalRandomTest.java @@ -0,0 +1,559 @@ +/* + * 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.Assert; +import org.testng.annotations.Test; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.BiConsumer; + +import static org.testng.Assert.*; + +/** + * @test + * @run testng ThreadLocalRandomTest + * @summary test methods on ThreadLocalRandom + */ +@Test +public class ThreadLocalRandomTest { + + // Note: this test was copied 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; + + /** + * setSeed throws UnsupportedOperationException + */ + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testSetSeed() { + ThreadLocalRandom.current().setSeed(17); + } + + /** + * Repeated calls to nextInt produce at least two distinct results + */ + public void testNextInt() { + int f = ThreadLocalRandom.current().nextInt(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextInt() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextLong produce at least two distinct results + */ + public void testNextLong() { + long f = ThreadLocalRandom.current().nextLong(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextLong() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextBoolean produce at least two distinct results + */ + public void testNextBoolean() { + boolean f = ThreadLocalRandom.current().nextBoolean(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextBoolean() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextFloat produce at least two distinct results + */ + public void testNextFloat() { + float f = ThreadLocalRandom.current().nextFloat(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextFloat() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextDouble produce at least two distinct results + */ + public void testNextDouble() { + double f = ThreadLocalRandom.current().nextDouble(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextDouble() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextGaussian produce at least two distinct results + */ + public void testNextGaussian() { + double f = ThreadLocalRandom.current().nextGaussian(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextGaussian() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * nextInt(negative) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextIntBoundedNeg() { + int f = ThreadLocalRandom.current().nextInt(-17); + } + + /** + * nextInt(least >= bound) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextIntBadBounds() { + int f = ThreadLocalRandom.current().nextInt(17, 2); + } + + /** + * nextInt(bound) returns 0 <= value < bound; repeated calls produce at + * least two distinct results + */ + public void testNextIntBounded() { + // sample bound space across prime number increments + for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) { + int f = ThreadLocalRandom.current().nextInt(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextInt(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextInt(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextIntBounded2() { + for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) { + int f = ThreadLocalRandom.current().nextInt(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextInt(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextLong(negative) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextLongBoundedNeg() { + long f = ThreadLocalRandom.current().nextLong(-17); + } + + /** + * nextLong(least >= bound) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextLongBadBounds() { + long f = ThreadLocalRandom.current().nextLong(17, 2); + } + + /** + * nextLong(bound) returns 0 <= value < bound; repeated calls produce at + * least two distinct results + */ + public void testNextLongBounded() { + for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) { + long f = ThreadLocalRandom.current().nextLong(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextLong(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextLong(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextLongBounded2() { + for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + long f = ThreadLocalRandom.current().nextLong(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextLong(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextDouble(bound) throws IllegalArgumentException + */ + public void testNextDoubleBadBound() { + ThreadLocalRandom r = ThreadLocalRandom.current(); + executeAndCatchIAE(() -> r.nextDouble(0.0)); + executeAndCatchIAE(() -> r.nextDouble(-0.0)); + executeAndCatchIAE(() -> r.nextDouble(+0.0)); + executeAndCatchIAE(() -> r.nextDouble(-1.0)); + executeAndCatchIAE(() -> r.nextDouble(Double.NaN)); + executeAndCatchIAE(() -> r.nextDouble(Double.NEGATIVE_INFINITY)); + + // Returns Double.MAX_VALUE +// executeAndCatchIAE(() -> r.nextDouble(Double.POSITIVE_INFINITY)); + } + + /** + * nextDouble(origin, bound) throws IllegalArgumentException + */ + public void testNextDoubleBadOriginBound() { + testDoubleBadOriginBound(ThreadLocalRandom.current()::nextDouble); + } + + // An arbitrary finite double value + static final double FINITE = Math.PI; + + void testDoubleBadOriginBound(BiConsumer bi) { + executeAndCatchIAE(() -> bi.accept(17.0, 2.0)); + executeAndCatchIAE(() -> bi.accept(0.0, 0.0)); + executeAndCatchIAE(() -> bi.accept(Double.NaN, FINITE)); + executeAndCatchIAE(() -> bi.accept(FINITE, Double.NaN)); + executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)); + + // Returns NaN +// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, FINITE)); +// executeAndCatchIAE(() -> bi.accept(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + + executeAndCatchIAE(() -> bi.accept(FINITE, Double.NEGATIVE_INFINITY)); + + // Returns Double.MAX_VALUE +// executeAndCatchIAE(() -> bi.accept(FINITE, Double.POSITIVE_INFINITY)); + + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)); + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, FINITE)); + executeAndCatchIAE(() -> bi.accept(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)); + } + + /** + * nextDouble(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextDoubleBounded2() { + for (double least = 0.0001; least < 1.0e20; least *= 8) { + for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) { + double f = ThreadLocalRandom.current().nextDouble(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + double j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextDouble(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * Invoking sized ints, long, doubles, with negative sizes throws + * IllegalArgumentException + */ + public void testBadStreamSize() { + ThreadLocalRandom r = ThreadLocalRandom.current(); + executeAndCatchIAE(() -> r.ints(-1L)); + executeAndCatchIAE(() -> r.ints(-1L, 2, 3)); + executeAndCatchIAE(() -> r.longs(-1L)); + executeAndCatchIAE(() -> r.longs(-1L, -1L, 1L)); + executeAndCatchIAE(() -> r.doubles(-1L)); + executeAndCatchIAE(() -> r.doubles(-1L, .5, .6)); + } + + /** + * Invoking bounded ints, long, doubles, with illegal bounds throws + * IllegalArgumentException + */ + public void testBadStreamBounds() { + ThreadLocalRandom r = ThreadLocalRandom.current(); + executeAndCatchIAE(() -> r.ints(2, 1)); + executeAndCatchIAE(() -> r.ints(10, 42, 42)); + executeAndCatchIAE(() -> r.longs(-1L, -1L)); + executeAndCatchIAE(() -> r.longs(10, 1L, -2L)); + + testDoubleBadOriginBound((o, b) -> r.doubles(10, o, b)); + } + + private void executeAndCatchIAE(Runnable r) { + executeAndCatch(IllegalArgumentException.class, r); + } + + private void executeAndCatch(Class expected, Runnable r) { + Exception caught = null; + try { + r.run(); + } + catch (Exception e) { + caught = e; + } + + assertNotNull(caught, + String.format("No Exception was thrown, expected an Exception of %s to be thrown", + expected.getName())); + Assert.assertTrue(expected.isInstance(caught), + String.format("Exception thrown %s not an instance of %s", + caught.getClass().getName(), expected.getName())); + } + + /** + * A parallel sized stream of ints generates the given number of values + */ + public void testIntsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.ints(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * A parallel sized stream of longs generates the given number of values + */ + public void testLongsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.longs(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * A parallel sized stream of doubles generates the given number of values + */ + public void testDoublesCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.doubles(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * Each of a parallel sized stream of bounded ints is within bounds + */ + public void testBoundedInts() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + 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).parallel(). + forEach(x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); + }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * Each of a parallel sized stream of bounded longs is within bounds + */ + public void testBoundedLongs() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + 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).parallel(). + forEach(x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); + }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * Each of a parallel sized stream of bounded doubles is within bounds + */ + public void testBoundedDoubles() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + 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).parallel(). + 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(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + 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(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + 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(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + 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(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + 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(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + 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(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.doubles().limit(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + +}