8248862: Implement Enhanced Pseudo-Random Number Generators

Reviewed-by: darcy
This commit is contained in:
Jim Laskey 2021-04-05 16:29:18 +00:00
parent 39719da9d1
commit a0ec2cb289
27 changed files with 11384 additions and 1772 deletions

View file

@ -25,11 +25,13 @@
package java.security;
import java.math.BigInteger;
import java.util.*;
import java.util.random.RandomGenerator;
import java.util.regex.*;
import java.security.Provider.Service;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
import sun.security.jca.*;
import sun.security.jca.GetInstance.Instance;
import sun.security.provider.SunEntries;
@ -147,6 +149,10 @@ import sun.security.util.Debug;
* @since 1.1
*/
@RandomGeneratorProperties(
name = "SecureRandom",
isStochastic = true
)
public class SecureRandom extends java.util.Random {
private static final Debug pdebug =

View file

@ -24,21 +24,26 @@
*/
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 jdk.internal.util.random.RandomSupport.AbstractSpliteratorGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
import jdk.internal.util.random.RandomSupport.RandomIntsSpliterator;
import jdk.internal.util.random.RandomSupport.RandomLongsSpliterator;
import jdk.internal.util.random.RandomSupport.RandomDoublesSpliterator;
import static jdk.internal.util.random.RandomSupport.*;
import jdk.internal.misc.Unsafe;
/**
* An instance of this class is used to generate a stream of
* pseudorandom numbers. The class uses a 48-bit seed, which is
* pseudorandom numbers; its period is only 2<sup>48</sup>.
* The class uses a 48-bit seed, which is
* modified using a linear congruential formula. (See Donald E. Knuth,
* <cite>The Art of Computer Programming, Volume 2, Third
* edition: Seminumerical Algorithms</cite>, Section 3.2.1.)
@ -74,7 +79,14 @@ import jdk.internal.misc.Unsafe;
* @author Frank Yellin
* @since 1.0
*/
public class Random implements java.io.Serializable {
@SuppressWarnings("exports")
@RandomGeneratorProperties(
name = "Random",
i = 48, j = 0, k = 0,
equidistribution = 0
)
public class Random extends AbstractSpliteratorGenerator
implements java.io.Serializable {
/** use serialVersionUID from JDK 1.1 for interoperability */
@java.io.Serial
static final long serialVersionUID = 3905348978240129619L;
@ -92,11 +104,6 @@ public class Random implements java.io.Serializable {
private static final double DOUBLE_UNIT = 0x1.0p-53; // 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
@ -118,17 +125,18 @@ public class Random implements java.io.Serializable {
}
private static final AtomicLong seedUniquifier
= new AtomicLong(8682522807148012L);
= new AtomicLong(8682522807148012L);
/**
* Creates a new random number generator using a single {@code long} seed.
* The seed is the initial value of the internal state of the pseudorandom
* number generator which is maintained by method {@link #next}.
*
* <p>The invocation {@code new Random(seed)} is equivalent to:
* <pre> {@code
* @implSpec The invocation {@code new Random(seed)} is equivalent to:
* <pre>{@code
* Random rnd = new Random();
* rnd.setSeed(seed);}</pre>
* rnd.setSeed(seed);
* }</pre>
*
* @param seed the initial seed
* @see #setSeed(long)
@ -211,9 +219,9 @@ public class Random implements java.io.Serializable {
* byte array. The number of random bytes produced is equal to
* the length of the byte array.
*
* <p>The method {@code nextBytes} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* @implSpec The method {@code nextBytes} is
* implemented by class {@code Random} as if by:
* <pre>{@code
* public void nextBytes(byte[] bytes) {
* for (int i = 0; i < bytes.length; )
* for (int rnd = nextInt(), n = Math.min(bytes.length - i, 4);
@ -225,90 +233,15 @@ public class Random implements java.io.Serializable {
* @throws NullPointerException if the byte array is null
* @since 1.1
*/
@Override
public void nextBytes(byte[] bytes) {
for (int i = 0, len = bytes.length; i < len; )
for (int rnd = nextInt(),
n = Math.min(len - i, Integer.SIZE/Byte.SIZE);
n = Math.min(len - i, Integer.SIZE/Byte.SIZE);
n-- > 0; rnd >>= Byte.SIZE)
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
@ -316,9 +249,9 @@ public class Random implements java.io.Serializable {
* pseudorandomly generated and returned. All 2<sup>32</sup> possible
* {@code int} values are produced with (approximately) equal probability.
*
* <p>The method {@code nextInt} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* @implSpec The method {@code nextInt} is
* implemented by class {@code Random} as if by:
* <pre>{@code
* public int nextInt() {
* return next(32);
* }}</pre>
@ -326,6 +259,7 @@ public class Random implements java.io.Serializable {
* @return the next pseudorandom, uniformly distributed {@code int}
* value from this random number generator's sequence
*/
@Override
public int nextInt() {
return next(32);
}
@ -337,9 +271,11 @@ public class Random implements java.io.Serializable {
* {@code nextInt} is that one {@code int} value in the specified range
* is pseudorandomly generated and returned. All {@code bound} possible
* {@code int} values are produced with (approximately) equal
* probability. The method {@code nextInt(int bound)} is implemented by
* probability.
*
* @implSpec The method {@code nextInt(int bound)} is implemented by
* class {@code Random} as if by:
* <pre> {@code
* <pre>{@code
* public int nextInt(int bound) {
* if (bound <= 0)
* throw new IllegalArgumentException("bound must be positive");
@ -384,15 +320,15 @@ public class Random implements java.io.Serializable {
* @throws IllegalArgumentException if bound is not positive
* @since 1.2
*/
@Override
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
throw new IllegalArgumentException(BAD_BOUND);
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 {
else { // reject over-represented candidates
for (int u = r;
u - (r = u % bound) + m < 0;
u = next(31))
@ -400,16 +336,15 @@ public class Random implements java.io.Serializable {
}
return r;
}
/**
* Returns the next pseudorandom, uniformly distributed {@code long}
* value from this random number generator's sequence. The general
* contract of {@code nextLong} is that one {@code long} value is
* pseudorandomly generated and returned.
*
* <p>The method {@code nextLong} is implemented by class {@code Random}
* @implSpec The method {@code nextLong} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* <pre>{@code
* public long nextLong() {
* return ((long)next(32) << 32) + next(32);
* }}</pre>
@ -420,6 +355,7 @@ public class Random implements java.io.Serializable {
* @return the next pseudorandom, uniformly distributed {@code long}
* value from this random number generator's sequence
*/
@Override
public long nextLong() {
// it's okay that the bottom word remains signed.
return ((long)(next(32)) << 32) + next(32);
@ -433,9 +369,9 @@ public class Random implements java.io.Serializable {
* values {@code true} and {@code false} are produced with
* (approximately) equal probability.
*
* <p>The method {@code nextBoolean} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* @implSpec The method {@code nextBoolean} is implemented by class
* {@code Random} as if by:
* <pre>{@code
* public boolean nextBoolean() {
* return next(1) != 0;
* }}</pre>
@ -445,6 +381,7 @@ public class Random implements java.io.Serializable {
* sequence
* @since 1.2
*/
@Override
public boolean nextBoolean() {
return next(1) != 0;
}
@ -462,21 +399,19 @@ public class Random implements java.io.Serializable {
* where <i>m</i> is a positive integer less than 2<sup>24</sup>, are
* produced with (approximately) equal probability.
*
* <p>The method {@code nextFloat} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* @implSpec The method {@code nextFloat} is implemented by class
* {@code Random} as if by:
* <pre>{@code
* public float nextFloat() {
* return next(24) / ((float)(1 << 24));
* }}</pre>
*
* <p>The hedge "approximately" is used in the foregoing description only
* because the next method is only approximately an unbiased source of
* independently chosen bits. If it were a perfect source of randomly
* chosen bits, then the algorithm shown would choose {@code float}
* values from the stated range with perfect uniformity.<p>
* [In early versions of Java, the result was incorrectly calculated as:
* <pre> {@code
* return next(30) / ((float)(1 << 30));}</pre>
* <pre> {@code return next(30) / ((float)(1 << 30));}</pre>
* This might seem to be equivalent, if not better, but in fact it
* introduced a slight nonuniformity because of the bias in the rounding
* of floating-point numbers: it was slightly more likely that the
@ -486,6 +421,7 @@ public class Random implements java.io.Serializable {
* value between {@code 0.0} and {@code 1.0} from this
* random number generator's sequence
*/
@Override
public float nextFloat() {
return next(24) / ((float)(1 << 24));
}
@ -500,35 +436,33 @@ public class Random implements java.io.Serializable {
* range {@code 0.0d} (inclusive) to {@code 1.0d} (exclusive), is
* pseudorandomly generated and returned.
*
* <p>The method {@code nextDouble} is implemented by class {@code Random}
* as if by:
* <pre> {@code
* @implSpec The method {@code nextDouble} is implemented by class
* {@code Random} as if by:
* <pre>{@code
* public double nextDouble() {
* return (((long)next(26) << 27) + next(27))
* / (double)(1L << 53);
* }}</pre>
*
* <p>The hedge "approximately" is used in the foregoing description only
* because the {@code next} method is only approximately an unbiased
* source of independently chosen bits. If it were a perfect source of
* randomly chosen bits, then the algorithm shown would choose
* {@code double} values from the stated range with perfect uniformity.
* because the {@code next} method is only approximately an unbiased source
* of independently chosen bits. If it were a perfect source of randomly
* chosen bits, then the algorithm shown would choose {@code double} values
* from the stated range with perfect uniformity.
* <p>[In early versions of Java, the result was incorrectly calculated as:
* <pre> {@code
* return (((long)next(27) << 27) + next(27))
* / (double)(1L << 54);}</pre>
* <pre> {@code return (((long)next(27) << 27) + next(27)) / (double)(1L << 54);}</pre>
* This might seem to be equivalent, if not better, but in fact it
* introduced a large nonuniformity because of the bias in the rounding
* of floating-point numbers: it was three times as likely that the
* low-order bit of the significand would be 0 than that it would be 1!
* This nonuniformity probably doesn't matter much in practice, but we
* strive for perfection.]
* introduced a large nonuniformity because of the bias in the rounding of
* floating-point numbers: it was three times as likely that the low-order
* bit of the significand would be 0 than that it would be 1! This
* nonuniformity probably doesn't matter much in practice, but we strive
* for perfection.]
*
* @return the next pseudorandom, uniformly distributed {@code double}
* value between {@code 0.0} and {@code 1.0} from this
* random number generator's sequence
* @see Math#random
*/
@Override
public double nextDouble() {
return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
}
@ -546,9 +480,9 @@ public class Random implements java.io.Serializable {
* normal distribution with mean {@code 0.0} and standard deviation
* {@code 1.0}, is pseudorandomly generated and returned.
*
* <p>The method {@code nextGaussian} is implemented by class
* @implSpec The method {@code nextGaussian} is implemented by class
* {@code Random} as if by a threadsafe version of the following:
* <pre> {@code
* <pre>{@code
* private double nextNextGaussian;
* private boolean haveNextNextGaussian = false;
*
@ -569,6 +503,7 @@ public class Random implements java.io.Serializable {
* return v1 * multiplier;
* }
* }}</pre>
*
* This uses the <i>polar method</i> of G. E. P. Box, M. E. Muller, and
* G. Marsaglia, as described by Donald E. Knuth in <cite>The Art of
* Computer Programming, Volume 2, third edition: Seminumerical Algorithms</cite>,
@ -581,6 +516,7 @@ public class Random implements java.io.Serializable {
* standard deviation {@code 1.0} from this random number
* generator's sequence
*/
@Override
public synchronized double nextGaussian() {
// See Knuth, TAOCP, Vol. 2, 3rd edition, Section 3.4.1 Algorithm C.
if (haveNextNextGaussian) {
@ -600,8 +536,110 @@ public class Random implements java.io.Serializable {
}
}
// stream methods, coded in a way intended to better isolate for
// maintenance purposes the small differences across forms.
/**
* Serializable fields for Random.
*
* @serialField seed long
* seed for random computations
* @serialField nextNextGaussian double
* next Gaussian to be returned
* @serialField haveNextNextGaussian boolean
* nextNextGaussian is valid
*/
@java.io.Serial
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("seed", Long.TYPE),
new ObjectStreamField("nextNextGaussian", Double.TYPE),
new ObjectStreamField("haveNextNextGaussian", Boolean.TYPE)
};
/**
* Reconstitute the {@code Random} instance from a stream (that is,
* deserialize it).
*
* @param s the {@code ObjectInputStream} from which data is read
*
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
ObjectInputStream.GetField fields = s.readFields();
// The seed is read in as {@code long} for
// historical reasons, but it is converted to an AtomicLong.
long seedVal = fields.get("seed", -1L);
if (seedVal < 0)
throw new java.io.StreamCorruptedException(
"Random: invalid seed");
resetSeed(seedVal);
nextNextGaussian = fields.get("nextNextGaussian", 0.0);
haveNextNextGaussian = fields.get("haveNextNextGaussian", false);
}
/**
* Save the {@code Random} instance to a stream.
*
* @param s the {@code ObjectOutputStream} to which data is written
*
* @throws IOException if an I/O error occurs
*/
@java.io.Serial
private synchronized void writeObject(ObjectOutputStream s)
throws IOException {
// set the values of the Serializable fields
ObjectOutputStream.PutField fields = s.putFields();
// The seed is serialized as a long for historical reasons.
fields.put("seed", seed.get());
fields.put("nextNextGaussian", nextNextGaussian);
fields.put("haveNextNextGaussian", haveNextNextGaussian);
// save them
s.writeFields();
}
// Support for resetting seed while deserializing
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long seedOffset;
static {
try {
seedOffset = unsafe.objectFieldOffset
(Random.class.getDeclaredField("seed"));
} catch (Exception ex) { throw new Error(ex); }
}
private void resetSeed(long seedVal) {
unsafe.putReferenceVolatile(this, seedOffset, new AtomicLong(seedVal));
}
// Methods required by class AbstractSpliteratorGenerator
/**
* @hidden
*/
@Override
public Spliterator.OfInt makeIntsSpliterator(long index, long fence, int origin, int bound) {
return new RandomIntsSpliterator(this, index, fence, origin, bound);
}
/**
* @hidden
*/
@Override
public Spliterator.OfLong makeLongsSpliterator(long index, long fence, long origin, long bound) {
return new RandomLongsSpliterator(this, index, fence, origin, bound);
}
/**
* @hidden
*/
@Override
public Spliterator.OfDouble makeDoublesSpliterator(long index, long fence, double origin, double bound) {
return new RandomDoublesSpliterator(this, index, fence, origin, bound);
}
/**
* Returns a stream producing the given {@code streamSize} number of
@ -616,13 +654,9 @@ public class Random implements java.io.Serializable {
* less than zero
* @since 1.8
*/
@Override
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);
return super.ints(streamSize);
}
/**
@ -638,11 +672,9 @@ public class Random implements java.io.Serializable {
* @return a stream of pseudorandom {@code int} values
* @since 1.8
*/
@Override
public IntStream ints() {
return StreamSupport.intStream
(new RandomIntsSpliterator
(this, 0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0),
false);
return super.ints();
}
/**
@ -677,16 +709,9 @@ public class Random implements java.io.Serializable {
* 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);
@Override
public IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound) {
return super.ints(streamSize, randomNumberOrigin, randomNumberBound);
}
/**
@ -722,13 +747,9 @@ public class Random implements java.io.Serializable {
* is greater than or equal to {@code randomNumberBound}
* @since 1.8
*/
@Override
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);
return super.ints(randomNumberOrigin, randomNumberBound);
}
/**
@ -744,13 +765,9 @@ public class Random implements java.io.Serializable {
* less than zero
* @since 1.8
*/
@Override
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);
return super.longs(streamSize);
}
/**
@ -766,11 +783,9 @@ public class Random implements java.io.Serializable {
* @return a stream of pseudorandom {@code long} values
* @since 1.8
*/
@Override
public LongStream longs() {
return StreamSupport.longStream
(new RandomLongsSpliterator
(this, 0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L),
false);
return super.longs();
}
/**
@ -810,16 +825,9 @@ public class Random implements java.io.Serializable {
* 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);
@Override
public LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound) {
return super.longs(streamSize, randomNumberOrigin, randomNumberBound);
}
/**
@ -860,13 +868,9 @@ public class Random implements java.io.Serializable {
* is greater than or equal to {@code randomNumberBound}
* @since 1.8
*/
@Override
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);
return super.longs(randomNumberOrigin, randomNumberBound);
}
/**
@ -883,13 +887,9 @@ public class Random implements java.io.Serializable {
* less than zero
* @since 1.8
*/
@Override
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);
return super.doubles(streamSize);
}
/**
@ -906,14 +906,12 @@ public class Random implements java.io.Serializable {
* @return a stream of pseudorandom {@code double} values
* @since 1.8
*/
@Override
public DoubleStream doubles() {
return StreamSupport.doubleStream
(new RandomDoublesSpliterator
(this, 0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0),
false);
return super.doubles();
}
/**
/**
* Returns a stream producing the given {@code streamSize} number of
* pseudorandom {@code double} values, each conforming to the given origin
* (inclusive) and bound (exclusive).
@ -934,22 +932,15 @@ public class Random implements java.io.Serializable {
* @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}
* @throws IllegalArgumentException if {@code streamSize} is less than zero,
* or {@code randomNumberOrigin} is not finite,
* or {@code randomNumberBound} is not finite, or {@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
(this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
false);
@Override
public DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound) {
return super.doubles(streamSize, randomNumberOrigin, randomNumberBound);
}
/**
@ -979,260 +970,8 @@ public class Random implements java.io.Serializable {
* is greater than or equal to {@code randomNumberBound}
* @since 1.8
*/
@Override
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);
}
}
}
/**
* Serializable fields for Random.
*
* @serialField seed long
* seed for random computations
* @serialField nextNextGaussian double
* next Gaussian to be returned
* @serialField haveNextNextGaussian boolean
* nextNextGaussian is valid
*/
@java.io.Serial
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("seed", Long.TYPE),
new ObjectStreamField("nextNextGaussian", Double.TYPE),
new ObjectStreamField("haveNextNextGaussian", Boolean.TYPE)
};
/**
* Reconstitute the {@code Random} instance from a stream (that is,
* deserialize it).
*
* @param s the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
ObjectInputStream.GetField fields = s.readFields();
// The seed is read in as {@code long} for
// historical reasons, but it is converted to an AtomicLong.
long seedVal = fields.get("seed", -1L);
if (seedVal < 0)
throw new java.io.StreamCorruptedException(
"Random: invalid seed");
resetSeed(seedVal);
nextNextGaussian = fields.get("nextNextGaussian", 0.0);
haveNextNextGaussian = fields.get("haveNextNextGaussian", false);
}
/**
* Save the {@code Random} instance to a stream.
*
* @param s the {@code ObjectOutputStream} to which data is written
* @throws IOException if an I/O error occurs
*/
@java.io.Serial
private synchronized void writeObject(ObjectOutputStream s)
throws IOException {
// set the values of the Serializable fields
ObjectOutputStream.PutField fields = s.putFields();
// The seed is serialized as a long for historical reasons.
fields.put("seed", seed.get());
fields.put("nextNextGaussian", nextNextGaussian);
fields.put("haveNextNextGaussian", haveNextNextGaussian);
// save them
s.writeFields();
}
// Support for resetting seed while deserializing
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long seedOffset;
static {
try {
seedOffset = unsafe.objectFieldOffset
(Random.class.getDeclaredField("seed"));
} catch (Exception ex) { throw new Error(ex); }
}
private void resetSeed(long seedVal) {
unsafe.putReferenceVolatile(this, seedOffset, new AtomicLong(seedVal));
return super.doubles(randomNumberOrigin, randomNumberBound);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -22,24 +22,24 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util;
import java.math.BigInteger;
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 java.util.stream.Stream;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A generator of uniform pseudorandom values applicable for use in
* (among other contexts) isolated parallel computations that may
* generate subtasks. Class {@code SplittableRandom} supports methods for
* producing pseudorandom numbers of type {@code int}, {@code long},
* and {@code double} with similar usages as for class
* A generator of uniform pseudorandom values (with period 2<sup>64</sup>)
* applicable for use in (among other contexts) isolated parallel
* computations that may generate subtasks. Class {@code SplittableRandom}
* supports methods for producing pseudorandom numbers of type {@code int},
* {@code long}, and {@code double} with similar usages as for class
* {@link java.util.Random} but differs in the following ways:
*
* <ul>
@ -51,16 +51,16 @@ import java.util.stream.StreamSupport;
* 3.31.1</a>.) These tests validate only the methods for certain
* types and ranges, but similar properties are expected to hold, at
* least approximately, for others as well. The <em>period</em>
* (length of any series of generated values before it repeats) is at
* least 2<sup>64</sup>.
* (length of any series of generated values before it repeats) is
* 2<sup>64</sup>. </li>
*
* <li>Method {@link #split} constructs and returns a new
* <li> Method {@link #split} constructs and returns a new
* SplittableRandom instance that shares no mutable state with the
* current instance. However, with very high probability, the
* values collectively generated by the two objects have the same
* statistical properties as if the same quantity of values were
* generated by a single thread using a single {@code
* SplittableRandom} object.
* SplittableRandom} object. </li>
*
* <li>Instances of SplittableRandom are <em>not</em> thread-safe.
* They are designed to be split, not shared, across threads. For
@ -71,7 +71,7 @@ import java.util.stream.StreamSupport;
*
* <li>This class provides additional methods for generating random
* streams, that employ the above techniques when used in {@code
* stream.parallel()} mode.
* stream.parallel()} mode.</li>
*
* </ul>
*
@ -80,13 +80,19 @@ import java.util.stream.StreamSupport;
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@systemProperty java.util.secureRandomSeed} is set to {@code true}.
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @author Guy Steele
* @author Doug Lea
* @since 1.8
*/
public final class SplittableRandom {
@SuppressWarnings("exports")
@RandomGeneratorProperties(
name = "SplittableRandom",
i = 64, j = 0, k = 0,
equidistribution = 1
)
public final class SplittableRandom extends AbstractSplittableGenerator {
/*
* Implementation Overview.
@ -160,12 +166,6 @@ public final class SplittableRandom {
*/
private static final long GOLDEN_GAMMA = 0x9e3779b97f4a7c15L;
/**
* The least non-zero value returned by nextDouble(). This value
* is scaled by a random value of 53 bits to produce a result.
*/
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53);
/**
* The seed. Updated only via method nextSeed.
*/
@ -186,6 +186,7 @@ public final class SplittableRandom {
/**
* Computes Stafford variant 13 of 64bit mix function.
* http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
*/
private static long mix64(long z) {
z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L;
@ -195,6 +196,7 @@ public final class SplittableRandom {
/**
* Returns the 32 high bits of Stafford variant 4 mix64 function as int.
* http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
*/
private static int mix32(long z) {
z = (z ^ (z >>> 33)) * 0x62a9d9ed799705f5L;
@ -203,6 +205,8 @@ public final class SplittableRandom {
/**
* Returns the gamma value to use for a new split instance.
* Uses the 64bit mix function from MurmurHash3.
* https://github.com/aappleby/smhasher/wiki/MurmurHash3
*/
private static long mixGamma(long z) {
z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; // MurmurHash3 mix constants
@ -219,141 +223,10 @@ public final class SplittableRandom {
return seed += gamma;
}
// IllegalArgumentException messages
static final String BAD_BOUND = "bound must be positive";
static final String BAD_RANGE = "bound must be greater than origin";
static final String BAD_SIZE = "size must be non-negative";
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen
= new AtomicLong(mix64(System.currentTimeMillis()) ^
mix64(System.nanoTime()));
// at end of <clinit> to survive static initialization circularity
static {
if (java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Boolean>() {
public Boolean run() {
return Boolean.getBoolean("java.util.secureRandomSeed");
}})) {
byte[] seedBytes = java.security.SecureRandom.getSeed(8);
long s = (long)seedBytes[0] & 0xffL;
for (int i = 1; i < 8; ++i)
s = (s << 8) | ((long)seedBytes[i] & 0xffL);
defaultGen.set(s);
}
}
/*
* Internal versions of nextX methods used by streams, as well as
* the public nextX(origin, bound) methods. These exist mainly to
* avoid the need for multiple versions of stream spliterators
* across the different exported forms of streams.
*/
/**
* 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) {
/*
* Four Cases:
*
* 1. If the arguments indicate unbounded form, act as
* nextLong().
*
* 2. If the range is an exact power of two, apply the
* associated bit mask.
*
* 3. If the range is positive, loop to avoid potential bias
* when the implicit nextLong() bound (2<sup>64</sup>) is not
* evenly divisible by the range. The loop rejects candidates
* computed from otherwise over-represented values. The
* expected number of iterations under an ideal generator
* varies from 1 to 2, depending on the bound. The loop itself
* takes an unlovable form. Because the first candidate is
* already available, we need a break-in-the-middle
* construction, which is concisely but cryptically performed
* within the while-condition of a body-less for loop.
*
* 4. Otherwise, the range cannot be represented as a positive
* long. The loop repeatedly generates unbounded longs until
* obtaining a candidate meeting constraints (with an expected
* number of iterations of less than two).
*/
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 r;
}
/**
* 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;
}
/**
* 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;
}
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/* ---------------- public methods ---------------- */
@ -375,7 +248,7 @@ public final class SplittableRandom {
* may, and typically does, vary across program invocations.
*/
public SplittableRandom() { // emulate defaultGen.split()
long s = defaultGen.getAndAdd(GOLDEN_GAMMA << 1);
long s = defaultGen.getAndAdd(2 * GOLDEN_GAMMA);
this.seed = mix64(s);
this.gamma = mixGamma(s + GOLDEN_GAMMA);
}
@ -399,186 +272,102 @@ public final class SplittableRandom {
}
/**
* Fills a user-supplied byte array with generated pseudorandom bytes.
*
* @param bytes the byte array to fill with pseudorandom bytes
* @throws NullPointerException if bytes is null
* @since 10
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @since 17
*/
public void nextBytes(byte[] bytes) {
int i = 0;
int len = bytes.length;
for (int words = len >> 3; words--> 0; ) {
long rnd = nextLong();
for (int n = 8; n--> 0; rnd >>>= Byte.SIZE)
bytes[i++] = (byte)rnd;
}
if (i < len)
for (long rnd = nextLong(); i < len; rnd >>>= Byte.SIZE)
bytes[i++] = (byte)rnd;
public SplittableRandom split(SplittableGenerator source) {
return new SplittableRandom(source.nextLong(), mixGamma(source.nextLong()));
}
/**
* Returns a pseudorandom {@code int} value.
*
* @return a pseudorandom {@code int} value
* @hidden
*/
@Override
public Spliterator.OfInt makeIntsSpliterator(long index, long fence, int origin, int bound) {
return super.makeIntsSpliterator(index, fence, origin, bound);
}
/**
* @hidden
*/
@Override
public Spliterator.OfLong makeLongsSpliterator(long index, long fence, long origin, long bound) {
return super.makeLongsSpliterator(index, fence, origin, bound);
}
/**
* @hidden
*/
@Override
public Spliterator.OfDouble makeDoublesSpliterator(long index, long fence, double origin, double bound) {
return super.makeDoublesSpliterator(index, fence, origin, bound);
}
@Override
public int nextInt() {
return mix32(nextSeed());
}
/**
* 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(BAD_BOUND);
// Specialize internalNextInt for origin 0
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 {@code int} value between the specified
* origin (inclusive) and the specified bound (exclusive).
*
* @param origin the least value returned
* @param bound the upper bound (exclusive)
* @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 int nextInt(int origin, int bound) {
if (origin >= bound)
throw new IllegalArgumentException(BAD_RANGE);
return internalNextInt(origin, bound);
}
/**
* Returns a pseudorandom {@code long} value.
*
* @return a pseudorandom {@code long} value
*/
@Override
public long nextLong() {
return mix64(nextSeed());
}
/**
* Returns a pseudorandom {@code long} value between zero (inclusive)
* and the specified bound (exclusive).
*
* @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
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @since 10
*/
public long nextLong(long bound) {
if (bound <= 0)
throw new IllegalArgumentException(BAD_BOUND);
// Specialize internalNextLong for origin 0
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;
@Override
public void nextBytes(byte[] bytes) {
super.nextBytes(bytes);
}
/**
* 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 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}
* {@inheritDoc}
* @implSpec {@inheritDoc}
* @since 17
*/
public long nextLong(long origin, long bound) {
if (origin >= bound)
throw new IllegalArgumentException(BAD_RANGE);
return internalNextLong(origin, bound);
@Override
public Stream<SplittableGenerator> splits() {
return super.splits();
}
/**
* Returns a pseudorandom {@code double} value between zero
* (inclusive) and one (exclusive).
*
* @return a pseudorandom {@code double} value between zero
* (inclusive) and one (exclusive)
* {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @implSpec {@inheritDoc}
* @since 17
*/
public double nextDouble() {
return (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT;
@Override
public Stream<SplittableGenerator> splits(long streamSize) {
return super.splits(streamSize, this);
}
/**
* 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
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @implSpec {@inheritDoc}
* @since 17
*/
public double nextDouble(double bound) {
if (!(bound > 0.0))
throw new IllegalArgumentException(BAD_BOUND);
double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound;
return (result < bound) ? result : // correct for rounding
Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
@Override
public Stream<SplittableGenerator> splits(SplittableGenerator source) {
return super.splits(Long.MAX_VALUE, source);
}
/**
* 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}
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @implSpec {@inheritDoc}
* @since 17
*/
public double nextDouble(double origin, double bound) {
if (!(origin < bound))
throw new IllegalArgumentException(BAD_RANGE);
return internalNextDouble(origin, bound);
@Override
public Stream<SplittableGenerator> splits(long streamSize, SplittableGenerator source) {
return super.splits(streamSize, source);
}
/**
* Returns a pseudorandom {@code boolean} value.
*
* @return a pseudorandom {@code boolean} value
*/
public boolean nextBoolean() {
return mix32(nextSeed()) < 0;
}
// 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 from this generator and/or
@ -589,13 +378,9 @@ public final class SplittableRandom {
* @throws IllegalArgumentException if {@code streamSize} is
* less than zero
*/
@Override
public IntStream ints(long streamSize) {
if (streamSize < 0L)
throw new IllegalArgumentException(BAD_SIZE);
return StreamSupport.intStream
(new RandomIntsSpliterator
(this, 0L, streamSize, Integer.MAX_VALUE, 0),
false);
return super.ints(streamSize);
}
/**
@ -607,11 +392,9 @@ public final class SplittableRandom {
*
* @return a stream of pseudorandom {@code int} values
*/
@Override
public IntStream ints() {
return StreamSupport.intStream
(new RandomIntsSpliterator
(this, 0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0),
false);
return super.ints();
}
/**
@ -629,16 +412,9 @@ public final class SplittableRandom {
* less than zero, or {@code randomNumberOrigin}
* is greater than or equal to {@code randomNumberBound}
*/
public IntStream ints(long streamSize, int randomNumberOrigin,
int randomNumberBound) {
if (streamSize < 0L)
throw new IllegalArgumentException(BAD_SIZE);
if (randomNumberOrigin >= randomNumberBound)
throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.intStream
(new RandomIntsSpliterator
(this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
false);
@Override
public IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound) {
return super.ints(streamSize, randomNumberOrigin, randomNumberBound);
}
/**
@ -656,13 +432,9 @@ public final class SplittableRandom {
* @throws IllegalArgumentException if {@code randomNumberOrigin}
* is greater than or equal to {@code randomNumberBound}
*/
@Override
public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
if (randomNumberOrigin >= randomNumberBound)
throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.intStream
(new RandomIntsSpliterator
(this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
false);
return super.ints(randomNumberOrigin, randomNumberBound);
}
/**
@ -675,13 +447,9 @@ public final class SplittableRandom {
* @throws IllegalArgumentException if {@code streamSize} is
* less than zero
*/
@Override
public LongStream longs(long streamSize) {
if (streamSize < 0L)
throw new IllegalArgumentException(BAD_SIZE);
return StreamSupport.longStream
(new RandomLongsSpliterator
(this, 0L, streamSize, Long.MAX_VALUE, 0L),
false);
return super.longs(streamSize);
}
/**
@ -693,11 +461,9 @@ public final class SplittableRandom {
*
* @return a stream of pseudorandom {@code long} values
*/
@Override
public LongStream longs() {
return StreamSupport.longStream
(new RandomLongsSpliterator
(this, 0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L),
false);
return super.longs();
}
/**
@ -715,16 +481,9 @@ public final class SplittableRandom {
* less than zero, or {@code randomNumberOrigin}
* is greater than or equal to {@code randomNumberBound}
*/
public LongStream longs(long streamSize, long randomNumberOrigin,
long randomNumberBound) {
if (streamSize < 0L)
throw new IllegalArgumentException(BAD_SIZE);
if (randomNumberOrigin >= randomNumberBound)
throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.longStream
(new RandomLongsSpliterator
(this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
false);
@Override
public LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound) {
return super.longs(streamSize, randomNumberOrigin, randomNumberBound);
}
/**
@ -742,13 +501,9 @@ public final class SplittableRandom {
* @throws IllegalArgumentException if {@code randomNumberOrigin}
* is greater than or equal to {@code randomNumberBound}
*/
@Override
public LongStream longs(long randomNumberOrigin, long randomNumberBound) {
if (randomNumberOrigin >= randomNumberBound)
throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.longStream
(new RandomLongsSpliterator
(this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
false);
return super.longs(randomNumberOrigin, randomNumberBound);
}
/**
@ -761,13 +516,9 @@ public final class SplittableRandom {
* @throws IllegalArgumentException if {@code streamSize} is
* less than zero
*/
@Override
public DoubleStream doubles(long streamSize) {
if (streamSize < 0L)
throw new IllegalArgumentException(BAD_SIZE);
return StreamSupport.doubleStream
(new RandomDoublesSpliterator
(this, 0L, streamSize, Double.MAX_VALUE, 0.0),
false);
return super.doubles(streamSize);
}
/**
@ -780,11 +531,9 @@ public final class SplittableRandom {
*
* @return a stream of pseudorandom {@code double} values
*/
@Override
public DoubleStream doubles() {
return StreamSupport.doubleStream
(new RandomDoublesSpliterator
(this, 0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0),
false);
return super.doubles();
}
/**
@ -802,16 +551,9 @@ public final class SplittableRandom {
* less than zero, or {@code randomNumberOrigin}
* is greater than or equal to {@code randomNumberBound}
*/
public DoubleStream doubles(long streamSize, double randomNumberOrigin,
double randomNumberBound) {
if (streamSize < 0L)
throw new IllegalArgumentException(BAD_SIZE);
if (!(randomNumberOrigin < randomNumberBound))
throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.doubleStream
(new RandomDoublesSpliterator
(this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
false);
@Override
public DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound) {
return super.doubles(streamSize, randomNumberOrigin, randomNumberBound);
}
/**
@ -829,187 +571,8 @@ public final class SplittableRandom {
* @throws IllegalArgumentException if {@code randomNumberOrigin}
* is greater than or equal to {@code randomNumberBound}
*/
@Override
public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
if (!(randomNumberOrigin < randomNumberBound))
throw new IllegalArgumentException(BAD_RANGE);
return StreamSupport.doubleStream
(new RandomDoublesSpliterator
(this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
false);
return super.doubles(randomNumberOrigin, randomNumberBound);
}
/**
* 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.
*/
private static final class RandomIntsSpliterator
implements Spliterator.OfInt {
final SplittableRandom rng;
long index;
final long fence;
final int origin;
final int bound;
RandomIntsSpliterator(SplittableRandom 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.split(), 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;
SplittableRandom r = rng;
int o = origin, b = bound;
do {
consumer.accept(r.internalNextInt(o, b));
} while (++i < f);
}
}
}
/**
* Spliterator for long streams.
*/
private static final class RandomLongsSpliterator
implements Spliterator.OfLong {
final SplittableRandom rng;
long index;
final long fence;
final long origin;
final long bound;
RandomLongsSpliterator(SplittableRandom 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.split(), 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;
SplittableRandom r = rng;
long o = origin, b = bound;
do {
consumer.accept(r.internalNextLong(o, b));
} while (++i < f);
}
}
}
/**
* Spliterator for double streams.
*/
private static final class RandomDoublesSpliterator
implements Spliterator.OfDouble {
final SplittableRandom rng;
long index;
final long fence;
final double origin;
final double bound;
RandomDoublesSpliterator(SplittableRandom 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.split(), 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;
SplittableRandom r = rng;
double o = origin, b = bound;
do {
consumer.accept(r.internalNextDouble(o, b));
} while (++i < f);
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,640 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.util.random;
import java.lang.reflect.Constructor;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Objects;
import java.util.function.Function;
import java.util.Map;
import java.util.random.RandomGenerator.ArbitrarilyJumpableGenerator;
import java.util.random.RandomGenerator.JumpableGenerator;
import java.util.random.RandomGenerator.LeapableGenerator;
import java.util.random.RandomGenerator.SplittableGenerator;
import java.util.random.RandomGenerator.StreamableGenerator;
import java.util.ServiceLoader;
import java.util.ServiceLoader.Provider;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* This is a factory class for generating multiple random number generators
* of a specific <a href="package-summary.html#algorithms">algorithm</a>.
* {@link RandomGeneratorFactory} also provides
* methods for selecting random number generator algorithms.
*
* A specific {@link RandomGeneratorFactory} can be located by using the
* {@link RandomGeneratorFactory#of(String)} method, where the argument string
* is the name of the <a href="package-summary.html#algorithms">algorithm</a>
* required. The method
* {@link RandomGeneratorFactory#all()} produces a non-empty {@link Stream} of all available
* {@link RandomGeneratorFactory RandomGeneratorFactorys} that can be searched
* to locate a {@link RandomGeneratorFactory} suitable to the task.
*
* There are three methods for constructing a RandomGenerator instance,
* depending on the type of initial seed required.
* {@link RandomGeneratorFactory#create(long)} is used for long
* seed construction,
* {@link RandomGeneratorFactory#create(byte[])} is used for byte[]
* seed construction, and
* {@link RandomGeneratorFactory#create()} is used for random seed
* construction. Example;
*
* <pre>{@code
* RandomGeneratorFactory<RandomGenerator> factory = RandomGeneratorFactory.of("Random");
*
* for (int i = 0; i < 10; i++) {
* new Thread(() -> {
* RandomGenerator random = factory.create(100L);
* System.out.println(random.nextDouble());
* }).start();
* }
* }</pre>
*
* RandomGeneratorFactory also provides methods describing the attributes (or properties)
* of a generator and can be used to select random number generator
* <a href="package-summary.html#algorithms">algorithms</a>.
* These methods are typically used in
* conjunction with {@link RandomGeneratorFactory#all()}. In this example, the code
* locates the {@link RandomGeneratorFactory} that produces
* {@link RandomGenerator RandomGenerators}
* with the highest number of state bits.
*
* <pre>{@code
* RandomGeneratorFactory<RandomGenerator> best = RandomGeneratorFactory.all()
* .sorted(Comparator.comparingInt(RandomGenerator::stateBits).reversed())
* .findFirst()
* .orElse(RandomGeneratorFactory.of("Random"));
* System.out.println(best.name() + " in " + best.group() + " was selected");
*
* RandomGenerator rng = best.create();
* System.out.println(rng.nextLong());
* }</pre>
*
* @since 17
*
* @see java.util.random
*
*/
public final class RandomGeneratorFactory<T extends RandomGenerator> {
/**
* Instance provider class of random number algorithm.
*/
private final Provider<? extends RandomGenerator> provider;
/**
* Provider RandomGeneratorProperties annotation.
*/
private volatile RandomGeneratorProperties properties;
/**
* Default provider constructor.
*/
private volatile Constructor<T> ctor;
/**
* Provider constructor with long seed.
*/
private Constructor<T> ctorLong;
/**
* Provider constructor with byte[] seed.
*/
private Constructor<T> ctorBytes;
private static class FactoryMapHolder {
static final Map<String, Provider<? extends RandomGenerator>> FACTORY_MAP = createFactoryMap();
/**
* Returns the factory map, lazily constructing map on first use.
*
* @return Map of RandomGeneratorFactory classes.
*/
private static Map<String, Provider<? extends RandomGenerator>> createFactoryMap() {
return ServiceLoader
.load(RandomGenerator.class)
.stream()
.filter(p -> !p.type().isInterface())
.collect(Collectors.toMap(p -> p.type().getSimpleName(), Function.identity()));
}
}
/**
* Private constructor.
*
* @param provider Provider class to wrap.
*/
private RandomGeneratorFactory(Provider<? extends RandomGenerator> provider) {
this.provider = provider;
}
/**
* Returns the factory map, lazily constructing map on first call.
*
* @return Map of RandomGeneratorFactory classes.
*/
private static Map<String, Provider<? extends RandomGenerator>> getFactoryMap() {
return FactoryMapHolder.FACTORY_MAP;
}
/**
* Return the annotation for the specified provider.
*
* @return RandomGeneratorProperties annotation for the specified provider.
*/
private RandomGeneratorProperties getProperties() {
if (properties == null) {
synchronized (provider) {
if (properties == null) {
properties = provider.type().getDeclaredAnnotation(RandomGeneratorProperties.class);
Objects.requireNonNull(properties, provider.type() + " missing annotation");
}
}
}
return properties;
}
/**
* Return true if the provider is a subclass of the category.
*
* @param category Interface category, sub-interface of {@link RandomGenerator}.
*
* @return true if the provider is a subclass of the category.
*/
private boolean isSubclass(Class<? extends RandomGenerator> category) {
return isSubclass(category, provider);
}
/**
* Return true if the provider is a subclass of the category.
*
* @param category Interface category, sub-interface of {@link RandomGenerator}.
* @param provider Provider that is being filtered.
*
* @return true if the provider is a subclass of the category.
*/
private static boolean isSubclass(Class<? extends RandomGenerator> category,
Provider<? extends RandomGenerator> provider) {
return provider != null && category.isAssignableFrom(provider.type());
}
/**
* Returns the provider matching name and category.
*
* @param name Name of RandomGenerator
* @param category Interface category, sub-interface of {@link RandomGenerator}.
*
* @return A provider matching name and category.
*
* @throws IllegalArgumentException if provider is not a subclass of category.
*/
private static Provider<? extends RandomGenerator> findProvider(String name,
Class<? extends RandomGenerator> category)
throws IllegalArgumentException {
Map<String, Provider<? extends RandomGenerator>> fm = getFactoryMap();
Provider<? extends RandomGenerator> provider = fm.get(name);
if (provider == null) {
throw new IllegalArgumentException("No implementation of the random number generator algorithm \"" +
name +
"\" is available");
} else if (!isSubclass(category, provider)) {
throw new IllegalArgumentException("The random number generator algorithm \"" +
name +
"\" is not implemented with the interface \"" +
category.getSimpleName() +
"\"");
}
return provider;
}
/**
* Returns a {@link RandomGenerator} that utilizes the {@code name}
* <a href="package-summary.html#algorithms">algorithm</a>.
*
* @param name Name of random number algorithm to use
* @param category Sub-interface of {@link RandomGenerator} to type check
* @param <T> Sub-interface of {@link RandomGenerator} to produce
*
* @return An instance of {@link RandomGenerator}
*
* @throws IllegalArgumentException when either the name or category is null
*/
static <T extends RandomGenerator> T of(String name, Class<T> category)
throws IllegalArgumentException {
@SuppressWarnings("unchecked")
T uncheckedRandomGenerator = (T)findProvider(name, category).get();
return uncheckedRandomGenerator;
}
/**
* Returns a {@link RandomGeneratorFactory} that will produce instances
* of {@link RandomGenerator} that utilizes the named algorithm.
*
* @param name Name of random number algorithm to use
* @param category Sub-interface of {@link RandomGenerator} to type check
* @param <T> Sub-interface of {@link RandomGenerator} to produce
*
* @return Factory of {@link RandomGenerator}
*
* @throws IllegalArgumentException when either the name or category is null
*/
static <T extends RandomGenerator> RandomGeneratorFactory<T> factoryOf(String name, Class<T> category)
throws IllegalArgumentException {
Provider<? extends RandomGenerator> uncheckedProvider = findProvider(name, category);
return new RandomGeneratorFactory<>(uncheckedProvider);
}
/**
* Fetch the required constructors for class of random number algorithm.
*
* @param randomGeneratorClass class of random number algorithm (provider)
*/
private void getConstructors(Class<? extends RandomGenerator> randomGeneratorClass) {
if (ctor == null) {
synchronized (provider) {
if (ctor == null) {
PrivilegedExceptionAction<Constructor<?>[]> ctorAction = randomGeneratorClass::getConstructors;
try {
Constructor<?>[] ctors = AccessController.doPrivileged(ctorAction);
Constructor<T> tmpCtor = null;
Constructor<T> tmpCtorLong = null;
Constructor<T> tmpCtorBytes = null;
for (Constructor<?> ctorGeneric : ctors) {
@SuppressWarnings("unchecked")
Constructor<T> ctorSpecific = (Constructor<T>) ctorGeneric;
final Class<?>[] parameterTypes = ctorSpecific.getParameterTypes();
if (parameterTypes.length == 0) {
tmpCtor = ctorSpecific;
} else if (parameterTypes.length == 1) {
Class<?> argType = parameterTypes[0];
if (argType == long.class) {
tmpCtorLong = ctorSpecific;
} else if (argType == byte[].class) {
tmpCtorBytes = ctorSpecific;
}
}
}
if (tmpCtor == null) {
throw new IllegalStateException("Random algorithm " + name() + " is missing a default constructor");
}
// Store specialized constructors first, guarded by ctor
ctorBytes = tmpCtorBytes;
ctorLong = tmpCtorLong;
ctor = tmpCtor;
} catch (PrivilegedActionException ex) {
// Do nothing
}
}
}
}
}
/**
* Ensure all the required constructors are fetched.
*/
private void ensureConstructors() {
getConstructors(provider.type());
}
/**
* Returns a {@link RandomGeneratorFactory} that can produce instances of
* {@link RandomGenerator} that utilize the {@code name}
* <a href="package-summary.html#algorithms">algorithm</a>.
*
* @implSpec Availability is determined by RandomGeneratorFactory using the
* service provider API to locate implementations of the RandomGenerator interface.
*
* @param name Name of random number generator
* <a href="package-summary.html#algorithms">algorithm</a>
* @param <T> Sub-interface of {@link RandomGenerator} to produce
*
* @return {@link RandomGeneratorFactory} of {@link RandomGenerator}
*
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the named algorithm is not found
*/
public static <T extends RandomGenerator> RandomGeneratorFactory<T> of(String name) {
Objects.requireNonNull(name);
@SuppressWarnings("unchecked")
RandomGeneratorFactory<T> factory =
(RandomGeneratorFactory<T>)factoryOf(name, RandomGenerator.class);
return factory;
}
/**
* Returns a {@link RandomGeneratorFactory} meeting the minimal requirement
* of having an algorithm whose state bits are greater than or equal 64.
*
* @implSpec Since algorithms will improve over time, there is no
* guarantee that this method will return the same algorithm over time.
*
* @return a {@link RandomGeneratorFactory}
*/
public static RandomGeneratorFactory<RandomGenerator> getDefault() {
return factoryOf("L32X64MixRandom", RandomGenerator.class);
}
/**
* Returns a stream of matching Providers.
*
* @param category {@link RandomGenerator} sub-interface class to filter
* @param <T> {@link RandomGenerator} sub-interface return type
*
* RandomGenerators that are marked as deprecated or are not properly configured are not included in the result.
*
* @implSpec Availability is determined by RandomGeneratorFactory using the service provider API
* to locate implementations of the RandomGenerator interface.
*
* @return Stream of matching {@link RandomGeneratorFactory RandomGeneratorFactory(s)}.
*
* @hidden
*/
public static <T extends RandomGenerator> Stream<RandomGeneratorFactory<T>> all(Class<T> category) {
Map<String, Provider<? extends RandomGenerator>> fm = getFactoryMap();
return fm.values()
.stream()
.filter(p -> isSubclass(category, p) &&
!p.type().isAnnotationPresent(Deprecated.class) &&
p.type().isAnnotationPresent(RandomGeneratorProperties.class))
.map(RandomGeneratorFactory::new);
}
/**
* Returns a non-empty stream of available {@link RandomGeneratorFactory RandomGeneratorFactory(s)}.
*
* RandomGenerators that are marked as deprecated or are not properly configured are not included in the result.
*
* @implSpec Availability is determined by RandomGeneratorFactory using the service provider API
* to locate implementations of the RandomGenerator interface.
*
* @return a non-empty stream of all available {@link RandomGeneratorFactory RandomGeneratorFactory(s)}.
*/
public static Stream<RandomGeneratorFactory<RandomGenerator>> all() {
Map<String, Provider<? extends RandomGenerator>> fm = getFactoryMap();
return fm.values()
.stream()
.filter(p -> !p.type().isAnnotationPresent(Deprecated.class) &&
p.type().isAnnotationPresent(RandomGeneratorProperties.class))
.map(RandomGeneratorFactory::new);
}
/**
* Return the name of the <a href="package-summary.html#algorithms">algorithm</a>
* used by the random number generator.
*
* @return Name of the <a href="package-summary.html#algorithms">algorithm</a>.
*/
public String name() {
return provider.type().getSimpleName();
}
/**
* Return the group name of the <a href="package-summary.html#algorithms">algorithm</a>
* used by the random number generator.
*
* @return Group name of the <a href="package-summary.html#algorithms">algorithm</a>.
*/
public String group() {
return getProperties().group();
}
/**
* Returns number of bits used by the <a href="package-summary.html#algorithms">algorithm</a>
* to maintain state of seed.
*
* @return number of bits used by the <a href="package-summary.html#algorithms">algorithm</a>
* to maintain state of seed.
*/
public int stateBits() {
RandomGeneratorProperties properties = getProperties();
int i = properties.i();
int k = properties.k();
return i == 0 && k == 0 ? Integer.MAX_VALUE : i + k;
}
/**
* Returns the equidistribution of the <a href="package-summary.html#algorithms">algorithm</a>.
*
* @return the equidistribution of the <a href="package-summary.html#algorithms">algorithm</a>.
*/
public int equidistribution() {
return getProperties().equidistribution();
}
/**
* Return the period of the <a href="package-summary.html#algorithms">algorithm</a>
* used by the random number generator.
* Returns BigInteger.ZERO if period is not determinable.
*
* @return BigInteger period.
*/
public BigInteger period() {
RandomGeneratorProperties properties = getProperties();
int i = properties.i();
int j = properties.j();
int k = properties.k();
if (i == 0 && j == 0 && k == 0) {
return BigInteger.ZERO;
} else {
return BigInteger.ONE.shiftLeft(i).subtract(BigInteger.valueOf(j)).shiftLeft(k);
}
}
/**
* Return true if random generator is computed using an arithmetic
* <a href="package-summary.html#algorithms">algorithm</a>
* and is statistically deterministic.
*
* @return true if random generator is statistical.
*/
public boolean isStatistical() {
return !getProperties().isStochastic();
}
/**
* Return true if random generator is computed using external or entropic
* sources as inputs.
*
* @return true if random generator is stochastic.
*/
public boolean isStochastic() {
return getProperties().isStochastic();
}
/**
* Return true if random generator uses a hardware device (HRNG) to produce
* entropic input.
*
* @return true if random generator is generated by hardware.
*/
public boolean isHardware() {
return getProperties().isHardware();
}
/**
* Return true if random generator can jump an arbitrarily specified distant
* point in the state cycle.
*
* @return true if random generator is arbitrarily jumpable.
*/
public boolean isArbitrarilyJumpable() {
return isSubclass(ArbitrarilyJumpableGenerator.class);
}
/**
* Return true if random generator can jump a specified distant point in
* the state cycle.
*
* @return true if random generator is jumpable.
*/
public boolean isJumpable() {
return isSubclass(JumpableGenerator.class);
}
/**
* Return true if random generator is jumpable and can leap to a very distant
* point in the state cycle.
*
* @return true if random generator is leapable.
*/
public boolean isLeapable() {
return isSubclass(LeapableGenerator.class);
}
/**
* Return true if random generator can be cloned into a separate object with
* the same properties but positioned further in the state cycle.
*
* @return true if random generator is splittable.
*/
public boolean isSplittable() {
return isSubclass(SplittableGenerator.class);
}
/**
* Return true if random generator can be used to create
* {@link java.util.stream.Stream Streams} of random numbers.
*
* @return true if random generator is streamable.
*/
public boolean isStreamable() {
return isSubclass(StreamableGenerator.class);
}
/**
* Return true if the implementation of RandomGenerator (algorithm) has been
* marked for deprecation.
*
* @implNote Random number generator algorithms evolve over time; new
* algorithms will be introduced and old algorithms will
* lose standing. If an older algorithm is deemed unsuitable
* for continued use, it will be marked as deprecated to indicate
* that it may be removed at some point in the future.
*
* @return true if the implementation of RandomGenerator (algorithm) has been
* marked for deprecation
*/
public boolean isDeprecated() {
return provider.type().isAnnotationPresent(Deprecated.class);
}
/**
* Create an instance of {@link RandomGenerator} based on
* <a href="package-summary.html#algorithms">algorithm</a> chosen.
*
* @return new in instance of {@link RandomGenerator}.
*
*/
public T create() {
try {
ensureConstructors();
return ctor.newInstance();
} catch (Exception ex) {
// Should never happen.
throw new IllegalStateException("Random algorithm " + name() + " is missing a default constructor", ex);
}
}
/**
* Create an instance of {@link RandomGenerator} based on
* <a href="package-summary.html#algorithms">algorithm</a> chosen
* providing a starting long seed. If long seed is not supported by an
* algorithm then the no argument form of create is used.
*
* @param seed long random seed value.
*
* @return new in instance of {@link RandomGenerator}.
*/
public T create(long seed) {
try {
ensureConstructors();
return ctorLong.newInstance(seed);
} catch (Exception ex) {
return create();
}
}
/**
* Create an instance of {@link RandomGenerator} based on
* <a href="package-summary.html#algorithms">algorithm</a> chosen
* providing a starting byte[] seed. If byte[] seed is not supported by an
* <a href="package-summary.html#algorithms">algorithm</a> then the no
* argument form of create is used.
*
* @param seed byte array random seed value.
*
* @return new in instance of {@link RandomGenerator}.
*
* @throws NullPointerException if seed is null.
*/
public T create(byte[] seed) {
Objects.requireNonNull(seed, "seed must not be null");
try {
ensureConstructors();
return ctorBytes.newInstance(seed);
} catch (Exception ex) {
return create();
}
}
}

View file

@ -0,0 +1,628 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This package contains classes and interfaces that support a generic API
* for random number generation.
*
* <p>These classes and interfaces support the definition and use of "random
* generators", a term covering what have traditionally been called "random
* number generators" as well as generators of other sorts of randomly chosen
* values (eg. booleans). These classes and interfaces cover not only
* deterministic (pseudorandom) algorithms but also generators of values that
* use some "truly random" physical source (stochastic algorithms perhaps making
* use of thermal noise, for example, or quantum-mechanical effects).
*
* <p> The principal interface is {@link RandomGenerator}, which provides
* methods for requesting individual values of type {@code int}, {@code long},
* {@code float}, {@code double}, or {@code boolean} chosen pseudorandomly
* from a uniform distribution; methods for requesting values of type
* {@code double} chosen pseudorandomly from a normal distribution or from an
* exponential distribution; and methods for creating streams of values of type
* {@code int}, {@code long}, or {@code double} chosen pseudorandomly from a
* uniform distribution (such streams are spliterator-based, allowing for
* parallel processing of their elements). There are also static factory methods
* for creating an instance of a specific random number generator algorithm
* given its name.
*
* <p> The principal supporting class is {@link RandomGeneratorFactory}. This
* can be used to generate multiple random number generators for a specific
* algorithm. {@link RandomGeneratorFactory} also provides methods for
* selecting random number generator algorithms. RandomGeneratorFactory
* registers implementations of {@link RandomGenerator} interface using the
* service provider API.
*
* <p> An important subsidiary interface is
* {@link RandomGenerator.StreamableGenerator}, which provides methods for
* creating spliterator-based streams of {@link RandomGenerator} objects,
* allowing for parallel processing of these objects using multiple threads.
* Unlike {@link java.util.Random}, most implementations of
* {@link RandomGenerator} are <i>not</i> thread-safe. The intent is that
* instances should not be shared among threads; rather, each thread should have
* its own random generator(s) to use. The various pseudorandom algorithms
* provided by this package are designed so that multiple instances will (with
* very high probability) behave as if statistically independent.
*
* <p> For many purposes, these are the only two interfaces that a consumer of
* pseudorandom values will need. There are also some more specialized
* interfaces that describe more specialized categories of random number
* generators {@link RandomGenerator.SplittableGenerator SplittableGenerator},
* {@link RandomGenerator.JumpableGenerator JumpableGenerator},
* {@link RandomGenerator.LeapableGenerator LeapableGenerator}, and
* {@link RandomGenerator.ArbitrarilyJumpableGenerator ArbitrarilyJumpableGenerator}
* that have specific strategies for creating statistically independent instances.
*
* <h2>Using the Random Number Generator Interfaces</h2>
*
* To get started, an application should first create one instance of a
* generator class. Assume that the contents of the package
* {@link java.util.random} has been imported:
*
* <blockquote>{@code import java.util.random.*;}</blockquote>
*
* Then one can choose a specific implementation by giving the name of a generator
* algorithm to the static method {@link RandomGenerator#of}, in which case the
* no-arguments constructor for that implementation is used:
*
* <blockquote>{@code RandomGenerator g = RandomGenerator.of("L64X128MixRandom");}</blockquote>
*
* For a single-threaded application, this is all that is needed. One can then
* invoke methods of {@code g} such as
* {@link RandomGenerator#nextLong nextLong()},
* {@link RandomGenerator#nextInt nextInt()},
* {@link RandomGenerator#nextFloat nextFloat()},
* {@link RandomGenerator#nextDouble nextDouble()} and
* {@link RandomGenerator#nextBoolean nextBoolean()} to generate individual
* randomly chosen values. One can also use the methods
* {@link RandomGenerator#ints ints()}, {@link RandomGenerator#longs longs()}
* and {@link RandomGenerator#doubles doubles()} to create streams of randomly
* chosen values. The methods
* {@link RandomGenerator#nextGaussian nextGaussian()} and
* {@link RandomGenerator#nextExponential nextExponential()} draw floating-point
* values from nonuniform distributions.
*
* <p> For a multi-threaded application, one can repeat the preceding steps
* to create additional {@linkplain RandomGenerator RandomGenerators}, but
* often it is preferable to use methods of the one single initially
* created generator to create others like it. (One reason is that some
* generator algorithms, if asked to create a new set of generators all at
* once, can make a special effort to ensure that the new generators are
* statistically independent.) If the initial generator implements the
* interface {@link RandomGenerator.StreamableGenerator}, then the method
* {@link RandomGenerator.StreamableGenerator#rngs rngs()} can be used to
* create a stream of generators. If this is a parallel stream, then it is
* easy to get parallel execution by using the
* {@link java.util.stream.Stream#map map()} method on the stream.
* <p> For a multi-threaded application that forks new threads dynamically,
* another approach is to use an initial generator that implements the interface
* {@link RandomGenerator.SplittableGenerator}, which is then considered to
* "belong" to the initial thread for its exclusive use; then whenever any
* thread needs to fork a new thread, it first uses the
* {@link RandomGenerator.SplittableGenerator#split split()} method of its own
* generator to create a new generator, which is then passed to the newly
* created thread for exclusive use by that new thread.
*
*
* <h2>Choosing a Random Number Generator Algorithm</h2>
*
* <p> There are three groups of random number generator algorithm provided
* in Java; Legacy group, LXM group and Xoroshiro/Xoshiro group.
*
* <p> The legacy group includes random number generators that existed
* before JDK 17; Random, ThreadLocalRandom, SplittableRandom and
* SecureRandom. Random (LCG) is the weakest of available algorithms and it
* is recommended that users migrate to newer algorithms. If an application
* requires a random number generator algorithm that is cryptographically
* secure, then it should continue to use an instance of the class {@link
* java.security.SecureRandom}.
*
* <p> The algorithms in the LXM group use a similar algorithm. The parameters
* of the algorithm can be found in algorithm name. The numbers indicate the
* number of bits in the lower and upper state bits respectively. Mix indicates
* the algorithm uses mix congruency. StarStar indicates use a double
* multiplier.
*
* <p> The algorithms in the Xoroshiro/Xoshiro are more traditional algorithms
* where the number in the name indicates the period.
*
* <p> For applications (such as physical simulation, machine learning, and
* games) that do not require a cryptographically secure algorithm, this package
* provides multiple implementations of interface {@link RandomGenerator} that
* provide trade-offs among speed, space, period, accidental correlation, and
* equidistribution properties.
*
* <p> For applications with no special requirements,
* {@code L64X128MixRandom} has a good balance among speed, space,
* and period, and is suitable for both single-threaded and multi-threaded
* applications when used properly (a separate instance for each thread).
*
* <p> If the application uses only a single thread, then
* {@code Xoroshiro128PlusPlus} is even smaller and faster, and
* certainly has a sufficiently long period.
*
* <p> For an application running in a 32-bit hardware environment and using
* only one thread or a small number of threads, {@code L32X64MixRandom} may be a good
* choice.
*
* <p> For an application that uses many threads that are allocated in one batch
* at the start of the computation, either a "jumpable" generator such as
* {@code Xoroshiro128PlusPlus} or
* {@code Xoshiro256PlusPlus} may be used, or a "splittable"
* generator such as {@code L64X128MixRandom} or
* {@code L64X256MixRandom} may be used.
*
* <p> For an application that creates many threads dynamically, perhaps through
* the use of spliterators, a "splittable" generator such as
* {@code L64X128MixRandom} or {@code L64X256MixRandom} is
* recommended. If the number of generators created dynamically may
* be very large (millions or more), then using generators such as
* {@code L128X128MixRandom} or {@code L128X256MixRandom},
* which use a 128-bit parameter rather than a 64-bit parameter for their LCG
* subgenerator, will make it much less likely that two instances use the same
* state cycle.
*
* <p> For an application that uses tuples of consecutively generated values, it
* may be desirable to use a generator that is <i>k</i>-equidistributed such
* that <i>k</i> is at least as large as the length of the tuples being
* generated. The generator {@code L64X256MixRandom} is provably
* 4-equidistributed, and {@code L64X1024MixRandom} is provably
* 16-equidistributed.
*
* <p> For applications that generate large permutations, it may be best to use
* a generator whose period is much larger than the total number of possible
* permutations; otherwise it will be impossible to generate some of the
* intended permutations. For example, if the goal is to shuffle a deck of 52
* cards, the number of possible permutations is 52! (52 factorial), which is
* larger than 2<sup>225</sup> (but smaller than 2<sup>226</sup>), so it may be
* best to use a generator whose period at least 2<sup>256</sup>, such as
* {@code L64X256MixRandom} or {@code L64X1024MixRandom}
* or {@code L128X256MixRandom} or
* {@code L128X1024MixRandom}. (It is of course also necessary to
* provide sufficiently many seed bits when the generator is initialized, or
* else it will still be impossible to generate some of the intended
* permutations.)
*
*
* <h2><a id="algorithms">Random Number Generator Algorithms Available</a></h2>
*
* These algorithms [in the table below] must be found with the current version
* of Java SE. A particular JDK implementation may recognize additional
* algorithms; check the JDK's documentation for details. The set of algorithm
* required by Java SE may be updated by changes to the Java SE specification.
* Over time, new algorithms may be added and old algorithms may be removed.
* <p>In addition, as another life-cycle phase, an algorithm may be {@linkplain
* RandomGeneratorFactory#isDeprecated() deprecated}. A deprecated algorithm is
* not recommended for use. If a required algorithm is deprecated, it may be
* removed in a future release. Due to advances in random number generator
* algorithm development and analysis, an algorithm may be deprecated during the
* lifetime of a particular Java SE release. Changing the deprecation status of
* an algorithm is <em>not</em> a specification change.
*
* <table style="padding:0px 20px 0px 0px">
* <caption>Available Algorithms</caption>
* <thead>
* <tr>
* <th style="text-align:left">Algorithm</th>
* <th style="text-align:left">Group</th>
* <th style="text-align:left">Period</th>
* <th style="text-align:right">StateBits</th>
* <th style="text-align:right">Equidistribution</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td style="text-align:left">L128X1024MixRandom</td>
* <td style="text-align:left">LXM</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(N*64).subtract(BigInteger.ONE).shiftLeft(128)</td>
* <td style="text-align:right">1152</td>
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <td style="text-align:left">L128X128MixRandom</td>
* <td style="text-align:left">LXM</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(128)</td>
* <td style="text-align:right">256</td>
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <td style="text-align:left">L128X256MixRandom</td>
* <td style="text-align:left">LXM</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE).shiftLeft(128)</td>
* <td style="text-align:right">384</td>
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <td style="text-align:left">L32X64MixRandom</td>
* <td style="text-align:left">LXM</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE).shiftLeft(32)</td>
* <td style="text-align:right">96</td>
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <td style="text-align:left">L64X1024MixRandom</td>
* <td style="text-align:left">LXM</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(N*64).subtract(BigInteger.ONE).shiftLeft(64)</td>
* <td style="text-align:right">1088</td>
* <td style="text-align:right">16</td>
* </tr>
* <tr>
* <td style="text-align:left">L64X128MixRandom</td>
* <td style="text-align:left">LXM</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(64)</td>
* <td style="text-align:right">192</td>
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <td style="text-align:left">L64X128StarStarRandom</td>
* <td style="text-align:left">LXM</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(64)</td>
* <td style="text-align:right">192</td>
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <td style="text-align:left">L64X256MixRandom</td>
* <td style="text-align:left">LXM</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE).shiftLeft(64)</td>
* <td style="text-align:right">320</td>
* <td style="text-align:right">4</td>
* </tr>
* <tr>
* <td style="text-align:left">Random</td>
* <td style="text-align:left">Legacy</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(48)</td>
* <td style="text-align:right">48</td>
* <td style="text-align:right">0</td>
* </tr>
* <tr>
* <td style="text-align:left">SplittableRandom</td>
* <td style="text-align:left">Legacy</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(64)</td>
* <td style="text-align:right">64</td>
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <td style="text-align:left">ThreadLocalRandom <sup>*</sup></td>
* <td style="text-align:left">Legacy</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(64)</td>
* <td style="text-align:right">64</td>
* <td style="text-align:right">1</td>
* </tr>
* <tr>
* <td style="text-align:left">Xoroshiro128PlusPlus</td>
* <td style="text-align:left">Xoroshiro</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE)</td>
* <td style="text-align:right">128</td>
* <td style="text-align:right">2</td>
* </tr>
* <tr>
* <td style="text-align:left">Xoshiro256PlusPlus</td>
* <td style="text-align:left">Xoshiro</td>
* <td style="text-align:left">BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE)</td>
* <td style="text-align:right">256</td>
* <td style="text-align:right">4</td>
* </tr>
* </tbody>
* </table>
*
* <p><sup>*</sup> ThreadLocalRandom can only be accessed via
* {@link java.util.concurrent.ThreadLocalRandom#current()}.
*
* <h2>Categories of Random Number Generator Algorithms</h2>
*
* Historically, most pseudorandom generator algorithms have been based on some
* sort of finite-state machine with a single, large cycle of states; when it is
* necessary to have multiple threads use the same algorithm simultaneously, the
* usual technique is to arrange for each thread to traverse a different region
* of the state cycle. These regions may be doled out to threads by starting
* with a single initial state and then using a "jump function" that travels a
* long distance around the cycle (perhaps 2<sup>64</sup> steps or more); the
* jump function is applied repeatedly and sequentially, to identify widely
* spaced states that are then doled out, one to each thread, to serve as the
* initial state for the generator to be used by that thread. This strategy is
* supported by the interface {@link RandomGenerator.JumpableGenerator}.
* Sometimes it is desirable to support two levels of jumping (by long distances
* and by <i>really</i> long distances); this strategy is supported by the
* interface {@link RandomGenerator.LeapableGenerator}. There is also an interface
* {@link RandomGenerator.ArbitrarilyJumpableGenerator} for algorithms that allow
* jumping along the state cycle by any user-specified distance. In this package,
* implementations of these interfaces include
* "Xoroshiro128PlusPlus", and
* "Xoshiro256PlusPlus".
*
* <p> A more recent category of "splittable" pseudorandom generator algorithms
* uses a large family of state cycles and makes some attempt to ensure that
* distinct instances use different state cycles; but even if two instances
* "accidentally" use the same state cycle, they are highly likely to traverse
* different regions parts of that shared state cycle. This strategy is
* supported by the interface {@link RandomGenerator.SplittableGenerator}.
* In this package, implementations of this interface include
* "L32X64MixRandom",
* "L64X128StarStarRandom",
* "L64X128MixRandom",
* "L64X256MixRandom",
* "L64X1024MixRandom",
* "L128X128MixRandom",
* "L128X256MixRandom", and
* "L128X1024MixRandom"; note that the class
* {@link java.util.SplittableRandom} also implements this interface.
*
*
* <h2>The LXM Family of Random Number Generator Algorithms</h2>
*
* The structure of the central nextLong (or nextInt) method of an LXM
* algorithm follows a suggestion in December 2017 by Sebastiano Vigna
* that using one LCG subgenerator and one xor-based subgenerator (rather
* than two LCG subgenerators) would provide a longer period, superior
* equidistribution, scalability, and better quality. Each of the
* specific implementations here combines one of the best currently known
* xor-based generators (xoroshiro or xoshiro, described by Blackman and
* Vigna in "Scrambled Linear Pseudorandom Number Generators", ACM
* Trans. Math. Softw., 2021) with an LCG that uses one of the best
* currently known multipliers (found by a search for better multipliers
* in 2019 by Steele and Vigna), and then applies a mixing function
* identified by Doug Lea. Testing has confirmed that the LXM algorithm
* is far superior in quality to the SplitMix algorithm (2014) used by
* SplittableRandom.
*
* Each class with a name of the form {@code L}<i>p</i>{@code X}<i>q</i>{@code
* SomethingRandom} uses some specific member of the LXM family of random number
* algorithms; "LXM" is short for "LCG, Xorshift, Mixing function". Every LXM
* generator consists of two subgenerators; one is an LCG (Linear Congruential
* Generator) and the other is an Xorshift generator. Each output of an LXM
* generator is the result of combining state from the LCG with state from the
* Xorshift generator by using a Mixing function (and then the state of the LCG
* and the state of the Xorshift generator are advanced).
*
* <p> The LCG subgenerator has an update step of the form {@code s = m*s + a},
* where {@code s}, {@code m}, and {@code a} are all binary integers of the same
* size, each having <i>p</i> bits; {@code s} is the mutable state, the
* multiplier {@code m} is fixed (the same for all instances of a class) and the
* addend {@code a} is a parameter (a final field of the instance). The
* parameter {@code a} is required to be odd (this allows the LCG to have the
* maximal period, namely 2<sup><i>p</i></sup>); therefore there are
* 2<sup><i>p</i>&minus;1</sup> distinct choices of parameter. (When the size of
* {@code s} is 128 bits, then we use the name "{@code sh}" below to refer to
* the high half of {@code s}, that is, the high-order 64 bits of {@code s}.)
*
* <p> The Xorshift subgenerator can in principle be any one of a wide variety
* of xorshift algorithms; in this package it is always either
* {@code xoroshiro128}, {@code xoshiro256}, or {@code xoroshiro1024}, in each
* case without any final scrambler such as "+" or "**". Its state consists of
* some fixed number of {@code int} or {@code long} fields, generally named
* {@code x0}, {@code x1}, and so on, which can take on any values provided that
* they are not all zero. The collective total size of these fields is <i>q</i>
* bits; therefore the period of this subgenerator is
* 2<sup><i>q</i></sup>&minus;1.
*
* <p> Because the periods 2<sup><i>p</i></sup> and 2<sup><i>q</i></sup>&minus;1
* of the two subgenerators are relatively prime, the <em>period</em> of any
* single instance of an LXM algorithm (the length of the series of generated
* values before it repeats) is the product of the periods of the subgenerators,
* that is, 2<sup><i>p</i></sup>(2<sup><i>q</i></sup>&minus;1), which is just
* slightly smaller than 2<sup>(<i>p</i>+<i>q</i>)</sup>. Moreover, if two
* distinct instances of the same LXM algorithm have different {@code a}
* parameters, then their cycles of produced values will be different.
*
* <p> Generally speaking, among the "{@code L}<i>p</i>{@code X}<i>q</i>"
* generators, the memory required for an instance is 2<i>p</i>+<i>q</i> bits.
* (If <i>q</i> is 1024 or larger, the Xorshift state is represented as an
* array, so additional bits are needed for the array object header, and another
* 32 bits are used for an array index.)
*
* <p> Larger values of <i>p</i> imply a lower probability that two distinct
* instances will traverse the same state cycle, and larger values of <i>q</i>
* imply that the generator is equidistributed in a larger number of dimensions
* (this is provably true when <i>p</i> is 64, and conjectured to be
* approximately true when <i>p</i> is 128). A class with "{@code Mix}" in its
* name uses a fairly strong mixing function with excellent avalanche
* characteristics; a class with "{@code StarStar}" in its name uses a weaker
* but faster mixing function.
*
* <p> The specific LXM algorithms used in this package are all chosen so that
* the 64-bit values produced by the {@link RandomGenerator#nextLong nextLong()}
* method are exactly equidistributed (for example, for any specific instance of
* "L64X128MixRandom", over the course of its cycle each of the
* 2<sup>64</sup> possible {@code long} values will be produced
* 2<sup>128</sup>&minus;1 times). The values produced by the
* {@link RandomGenerator#nextInt nextInt()},
* {@link RandomGenerator#nextFloat nextFloat()}, and
* {@link RandomGenerator#nextDouble nextDouble()} methods are likewise exactly
* equidistributed. Some algorithms provide a further guarantee of
* <i>k</i>-equidistribution for some <i>k</i> greater than 1, meaning that successive
* non-overlapping <i>k</i>-tuples of 64-bit values produced by the
* {@link RandomGenerator#nextLong nextLong()} method are exactly
* equidistributed (equally likely to occur).
*
* <p> The following table gives the period, state size (in bits), parameter
* size (in bits, including the low-order bit that is required always to be a
* 1-bit), and equidistribution property for each of the specific LXM algorithms
* used in this package.
*
* <table style="padding:0px 20px 0px 0px">
* <caption>Algorithm Properties</caption>
* <thead>
* <tr><th style="text-align:left">Implementation</th>
* <th style="text-align:right">Period</th>
* <th style="text-align:right">State size</th>
* <th style="text-align:right">Parameter size</th>
* <th style="text-align:left">{@link RandomGenerator#nextLong nextLong()} values are</th></tr>
* </thead>
* <tbody>
* <tr><td style="text-align:left">"L32X64MixRandom"</td>
* <td style="text-align:right">2<sup>32</sup>(2<sup>64</sup>&minus;1)</td>
* <td style="text-align:right">96 bits</td>
* <td style="text-align:right">32 bits</td>
* <td style="text-align:left"></td></tr>
* <tr><td style="text-align:left">"L64X128StarStarRandom"</td>
* <td style="text-align:right">2<sup>64</sup>(2<sup>128</sup>&minus;1)</td>
* <td style="text-align:right">192 bits</td>
* <td style="text-align:right">64 bits</td>
* <td style="text-align:left">2-equidistributed and exactly equidistributed</td></tr>
* <tr><td style="text-align:left">"L64X128MixRandom"</td>
* <td style="text-align:right">2<sup>64</sup>(2<sup>128</sup>&minus;1)</td>
* <td style="text-align:right">192 bits</td>
* <td style="text-align:right">64 bits</td>
* <td style="text-align:left">2-equidistributed and exactly equidistributed</td></tr>
* <tr><td style="text-align:left">"L64X256MixRandom"</td>
* <td style="text-align:right">2<sup>64</sup>(2<sup>256</sup>&minus;1)</td>
* <td style="text-align:right">320 bits</td>
* <td style="text-align:right">64 bits</td>
* <td style="text-align:left">4-equidistributed and exactly equidistributed</td></tr>
* <tr><td style="text-align:left">"L64X1024MixRandom"</td>
* <td style="text-align:right">2<sup>64</sup>(2<sup>1024</sup>&minus;1)</td>
* <td style="text-align:right">1088 bits</td>
* <td style="text-align:right">64 bits</td>
* <td style="text-align:left">16-equidistributed and exactly equidistributed</td></tr>
* <tr><td style="text-align:left">"L128X128MixRandom"</td>
* <td style="text-align:right">2<sup>128</sup>(2<sup>128</sup>&minus;1)</td>
* <td style="text-align:right">256 bits</td>
* <td style="text-align:right">128 bits</td>
* <td style="text-align:left">exactly equidistributed</td></tr>
* <tr><td style="text-align:left">"L128X256MixRandom"</td>
* <td style="text-align:right">2<sup>128</sup>(2<sup>256</sup>&minus;1)</td>
* <td style="text-align:right">384 bits</td>
* <td style="text-align:right">128 bits</td>
* <td style="text-align:left">exactly equidistributed</td></tr>
* <tr><td style="text-align:left">"L128X1024MixRandom"</td>
* <td style="text-align:right">2<sup>128</sup>(2<sup>1024</sup>&minus;1)</td>
* <td style="text-align:right">1152 bits</td>
* <td style="text-align:right">128 bits</td>
* <td style="text-align:left">exactly equidistributed</td></tr>
* </tbody>
* </table>
*
* For the algorithms listed above whose names begin with {@code L32}, the
* 32-bit values produced by the {@link RandomGenerator#nextInt nextInt()}
* method are exactly equidistributed, but the 64-bit values produced by the
* {@link RandomGenerator#nextLong nextLong()} method are not exactly
* equidistributed.
*
* <p> For the algorithms listed above whose names begin with {@code L64} or
* {@code L128}, the 64-bit values produced by the
* {@link RandomGenerator#nextLong nextLong()} method are <i>exactly
* equidistributed</i>: every instance, over the course of its cycle, will
* produce each of the 2<sup>64</sup> possible {@code long} values exactly the
* same number of times. For example, any specific instance of
* "L64X256MixRandom", over the course of its cycle each of the
* 2<sup>64</sup> possible {@code long} values will be produced
* 2<sup>256</sup>&minus;1 times. The values produced by the
* {@link RandomGenerator#nextInt nextInt()},
* {@link RandomGenerator#nextFloat nextFloat()}, and
* {@link RandomGenerator#nextDouble nextDouble()} methods are likewise exactly
* equidistributed.
*
* <p> In addition, for the algorithms listed above whose names begin with
* {@code L64}, the 64-bit values produced by the
* {@link RandomGenerator#nextLong nextLong()} method are
* <i>k</i>-equidistributed (but not exactly <i>k</i>-equidistributed). To be
* precise, and taking "L64X256MixRandom" as an example: for
* any specific instance of "L64X256MixRandom", consider the
* (overlapping) length-4 subsequences of the cycle of 64-bit values produced by
* {@link RandomGenerator#nextLong nextLong()} (assuming no other methods are
* called that would affect the state). There are
* 2<sup>64</sup>(2<sup>256</sup>&minus;1) such subsequences, and each
* subsequence, which consists of 4 64-bit values, can have one of
* 2<sup>256</sup> values. Of those 2<sup>256</sup> subsequence values, nearly
* all of them (2<sup>256</sup>%minus;2<sup>64</sup>) occur 2<sup>64</sup> times
* over the course of the entire cycle, and the other 2<sup>64</sup> subsequence
* values occur only 2<sup>64</sup>&minus;1 times. So the ratio of the
* probability of getting any specific one of the less common subsequence values
* and the probability of getting any specific one of the more common
* subsequence values is 1&minus;2<sup>-64</sup>. (Note that the set of
* 2<sup>64</sup> less-common subsequence values will differ from one instance
* of "L64X256MixRandom" to another, as a function of the
* additive parameter of the LCG.) The values produced by the
* {@link RandomGenerator#nextInt nextInt()},
* {@link RandomGenerator#nextFloat nextFloat()}, and
* {@link RandomGenerator#nextDouble nextDouble()} methods are likewise
* 4-equidistributed (but not exactly 4-equidistributed).
*
* <p> The next table gives the LCG multiplier value, the name of the specific
* Xorshift algorithm used, the specific numeric parameters for that Xorshift
* algorithm, and the mixing function for each of the specific LXM algorithms
* used in this package. (Note that the multiplier used for the 128-bit LCG
* cases is 65 bits wide, so the constant {@code 0x1d605bbb58c8abbfdL} shown in
* the table cannot actually be used in code; instead, only the 64 low-order
* bits {@code 0xd605bbb58c8abbfdL} are represented in the source code, and the
* missing 1-bit is handled through special coding of the multiply-add algorithm
* used in the LCG.)
*
* <table style="padding:0px 20px 0px 0px">
* <caption>LXM Multipliers</caption>
* <thead>
* <tr><th style="text-align:left">Implementation</th>
* <th style="text-align:right">LCG multiplier {@code m}</th>
* <th style="text-align:left">Xorshift algorithm</th>
* <th style="text-align:left">Xorshift parameters</th>
* <th style="text-align:left">Mixing function</th></tr>
* </thead>
* <tbody>
* <tr><td style="text-align:left">"L32X64MixRandom"</td>
* <td style="text-align:right">{@code 0xadb4a92d}</td>
* <td style="text-align:left">{@code xoroshiro64}, version 1.0</td>
* <td style="text-align:left">{@code (26, 9, 13)}</td>
* <td style="text-align:left">mixLea32{@code (s+x0)}</td></tr>
* <tr><td style="text-align:left">"L64X128StarStarRandom" </td>
* <td style="text-align:right">{@code 0xd1342543de82ef95L}</td>
* <td style="text-align:left">{@code xoroshiro128}, version 1.0</td>
* <td style="text-align:left">{@code (24, 16, 37)}</td>
* <td style="text-align:left">{@code Long.rotateLeft((s+x0)* 5, 7) * 9}</td></tr>
* <tr><td style="text-align:left">"L64X128MixRandom"</td>
* <td style="text-align:right">{@code 0xd1342543de82ef95L}</td>
* <td style="text-align:left">{@code xoroshiro128}, version 1.0</td>
* <td style="text-align:left">{@code (24, 16, 37)}</td>
* <td style="text-align:left">mixLea32{@code (s+x0)}</td></tr>
* <tr><td style="text-align:left">"L64X256MixRandom"</td>
* <td style="text-align:right">{@code 0xd1342543de82ef95L}</td>
* <td style="text-align:left">{@code xoshiro256}, version 1.0</td>
* <td style="text-align:left">{@code (17, 45)}</td>
* <td style="text-align:left">mixLea32{@code (s+x0)}</td></tr>
* <tr><td style="text-align:left">"L64X1024MixRandom"</td>
* <td style="text-align:right">{@code 0xd1342543de82ef95L}</td>
* <td style="text-align:left">{@code xoroshiro1024}, version 1.0</td>
* <td style="text-align:left">{@code (25, 27, 36)}</td>
* <td style="text-align:left">mixLea32{@code (s+x0)}</td></tr>
* <tr><td style="text-align:left">"L128X128MixRandom"</td>
* <td style="text-align:right">{@code 0x1d605bbb58c8abbfdL}</td>
* <td style="text-align:left">{@code xoroshiro128}, version 1.0</td>
* <td style="text-align:left">{@code (24, 16, 37)}</td>
* <td style="text-align:left">mixLea32{@code (sh+x0)}</td></tr>
* <tr><td style="text-align:left">"L128X256MixRandom"</td>
* <td style="text-align:right">{@code 0x1d605bbb58c8abbfdL}</td>
* <td style="text-align:left">{@code xoshiro256}, version 1.0</td>
* <td style="text-align:left">{@code (17, 45)}</td>
* <td style="text-align:left">mixLea32{@code (sh+x0)}</td></tr>
* <tr><td style="text-align:left">"L128X1024MixRandom"</td>
* <td style="text-align:right">{@code 0x1d605bbb58c8abbfdL}</td>
* <td style="text-align:left">{@code xoroshiro1024}, version 1.0</td>
* <td style="text-align:left">{@code (25, 27, 36)}</td>
* <td style="text-align:left">mixLea32{@code (sh+x0)}</td></tr>
* </tbody>
* </table>
*
* @since 17
*/
package java.util.random;

File diff suppressed because it is too large Load diff

View file

@ -113,6 +113,7 @@ module java.base {
exports java.util.concurrent.locks;
exports java.util.function;
exports java.util.jar;
exports java.util.random;
exports java.util.regex;
exports java.util.spi;
exports java.util.stream;
@ -248,6 +249,8 @@ module java.base {
jdk.jfr;
exports jdk.internal.util.xml.impl to
jdk.jfr;
exports jdk.internal.util.random to
jdk.random;
exports sun.net to
java.net.http,
jdk.naming.dns;
@ -356,7 +359,6 @@ module java.base {
exports jdk.internal.invoke to
jdk.incubator.foreign;
// the service types defined by the APIs in this module
uses java.lang.System.LoggerFinder;
@ -377,6 +379,7 @@ module java.base {
uses java.time.chrono.AbstractChronology;
uses java.time.chrono.Chronology;
uses java.time.zone.ZoneRulesProvider;
uses java.util.random.RandomGenerator;
uses java.util.spi.CalendarDataProvider;
uses java.util.spi.CalendarNameProvider;
uses java.util.spi.CurrencyNameProvider;
@ -400,4 +403,10 @@ module java.base {
provides java.nio.file.spi.FileSystemProvider with
jdk.internal.jrtfs.JrtFileSystemProvider;
provides java.util.random.RandomGenerator with
java.security.SecureRandom,
java.util.Random,
java.util.SplittableRandom;
}

View file

@ -0,0 +1,688 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This program computes the parameters and arrays needed by the modified-ziggurat algorithm
// for sampling from either an exponential distribution with mean 1 or a normal distribution
// with mean 0 and standad deviation 1. The four arrays needed for either kind of sampler are:
//
// X[i] is the horizontal width of ziggurat layer i
// Y[i] is f(X[i]), where f is the function for the exponential or normal curve
// alias_threshold is the table of probability mass thresholds for Walker's alias method,
// with one entry for the tail of the distributon and one entry for each overhang region
// alias_map is the table of forwarding indices used for Walker's alias method
//
// The four parameters needed by the exponential sampler are:
//
// exponential_number_of_layers the number of layers in the ziggurat
// exponential_X_0 the width of the box in layer 0 (which is the x-coordinate of the left end of the tail)
// exponential_convex_margin the maximum discrepancy between the curve and a certain diagonal line above it
//
// The five parameters needed by the normal sampler are:
//
// normal_number_of_layers the number of layers in the ziggurat
// normal_X_0 the width of the box in layer 0 (which is the x-coordinate of the left end of the tail)
// normal_inflection_index the index of the layer containing the inflection point
// normal_convex_margin the maximum discrepancy between the curve and a certain diagonal line above it
// normal_concave_margin the maximum discrepancy between the curve and a certain diagonal line below it
//
// After computing the parameters and tables, the program prints (to standard output)
// a complete Java source code file for a class named either FloatZigguratTables or
// DoubleZigguratTables, according to which precision has been requested.
// The only reason this program has been written as C code rather than Java code is that
// most of the calculations need to be performed in long double precision in order to
// be able to calculate double values of sufficient accuracy. This code relies on
// long double math functions sqrtl, powl, expl, logl, log2l, erfl, ceill, and copysignl.
// The overall modified ziggurat algorithm closely follows the description in:
//
// Christopher D. McFarland. 2016 (published online 24 Jun 2015). A modified ziggurat
// algorithm for generating exponentially and normally distributed pseudorandom numbers.
// Journal of Statistical Computation and Simulation 86 (7), pages 1281-1294.
// https://www.tandfonline.com/doi/abs/10.1080/00949655.2015.1060234
// Also at https://arxiv.org/abs/1403.6870 (26 March 2014).
//
// This paper in turn refers to code available at https://bitbucket.org/cdmcfarland/fast_prng.
// This includes a file create_layers.py of Python code for constructing the tables.
// The C code here loosely follows the organization of that Python code. However, the Python
// code appears to contain a number of errors and infelicities that have been corrected here:
//
// (1) On line 211, 1 is added to i_inflection, causing the value 205 to be printed when
// table size is 256. Adding 1 is not correct; the correct value for printing is 204.
//
// (2) On line 203, 3 values are dropped from the front of the array E when computing iE_max,
// with no explanation given. We believe this is incorrect; E[3:] should be simply E.
//
// (3) When the table elements are converted to printable strings using "map(str,data)",
// precision is lost because the Python str function produces only 12 decimal digits.
// In this C code, we print table entries using 17 decimal digits (format %23.16e),
// because 17 decimal digits suffice to preserve the value of any double precision
// value (and 16 decimal digits do not always suffice).
//
// (4) At lines 215-223, the Python code computes only a single E value for the
// rectangle containing the inflection point of the normal distribution curve.
// We believe it is conceptually more correct to compute two such E values,
// one for the concave part of the curve (to the left of the inflection point)
// and one for the convex part of the curve (to the right of the inflection point).
//
// We also observe that the McFarland paper asserts that the solver uses Brent's method,
// but the solver in the Python code does not implement Brent's method. A proper
// implementation of Brent's method (or its predecessor, Dekker's method) alternates
// between use of the Secant Method and use of the Bisection Method according to various
// criteria, but the Python code merely tries the Secant Method for a fixed number of
// iterations and then switches to the Bisection Method for a calculated number of iterations.
// Here we have translated Brent's Method into C from the Algol code in Brent's original paper.
#include <float.h>
#include <math.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
// The SIZE may be any power of 2 not greater than 2048; 128, 256, 512, and 1024
// are all plausible choices, but 256 probably makes the best space/time tradeoff.
// The number of layers in the constructed ziggurat will be slightly smaller than this.
#ifndef SIZE
#define SIZE 256
#endif
// Set USE_DOUBLE to 1 for Java routines that compute results of type double, or to 0 for float.
#ifndef USE_DOUBLE
#define USE_DOUBLE 1
#endif
#if USE_DOUBLE
typedef int64_t int_type;
typedef uint64_t uint_type;
typedef double float_type;
#define int_bits 64
#define max_int 0x7fffffffffffffff
#define max_uint 0xffffffffffffffff
#define java_int_type "long"
#define java_float_type "double"
#define java_capitalized_float_type "Double"
#else
typedef int32_t int_type;
typedef uint32_t uint_type;
typedef float float_type;
#define int_bits 32
#define max_int 0x7fffffff
#define max_uint 0xffffffff
#define java_int_type "int"
#define java_float_type "float"
#define java_capitalized_float_type "Float"
#endif
// We set SOLVER_TOLERANCE quite tight
#define SOLVER_TOLERANCE 1.0e-19L
#define PI (3.1415926535897932384626433832795L)
// Assert that two long double values are equal to within double (not long double) precision
#define check_equal(x, y) do assert(((x)>=(y) ? (x)-(y) : (y)-(x)) < DBL_EPSILON); while (0)
// The best way to compute the absolute value of a long double.
long double absl(long double x) {
return copysignl(x, 1.0);
}
// The type of a function that accepts one long double argument and returns a long double result.
typedef long double (*longdoublefn)(long double);
// The functions we will traffic in for solving need an argument but also two
// or three parameters, of which the first is a longdoublefn and the others are
// long double values. Because vanilla C doesn't have closures, we just arrange
// for the solver to accept three parameters and pass them in each time.
typedef long double (*solverfn)(long double, longdoublefn, long double, long double);
// The solver: find a root of function g (which has f, p1, and p2 as parameters).
// Returns a value x within bounds [a, b] such that g(x) is (close to) zero.
// Returns NaN if either a >= b or g(a) and g(b) have the same sign;
// this information can help the caller to adjust the bounds and try again.
//
// This solver uses Brent's Method, as it appears in:
//
// R. P. Brent. 1971. An algorithm with guaranteed convergence for finding a zero of a function.
// The Computer Journal, Volume 14, Issue 4, 422425. https://doi.org/10.1093/comjnl/14.4.422
//
// We assume that LDBL_EPSILON is the correct value to use for "macheps" as used in the Algol code.
long double fsolve(solverfn g, longdoublefn f, long double p1, long double p2,
long double a, long double b) {
// Check the required conditions on the arguments.
if (a >= b) return NAN;
long double ga = g(a, f, p1, p2), gb = g(b, f, p1, p2);
if (copysignl(1.0, ga) == copysignl(1.0, gb)) return NAN;
// Here is Brent's Method, translated from Algol to C. We have replaced the uses
// of "goto" with "for" loops and have scoped the variable declarations more tightly.
for (;;) { // label "int:" in the Algol code
long double c = a, gc = ga;
long double e = b - a;
long double d = e;
for (;;) { // label "ext:" in the Algol code
if (absl(gc) < absl(gb)) {
a = b; b = c; c = a;
ga = gb; gb = gc; gc = ga;
}
long double tol = 2 * LDBL_EPSILON * absl(b) + SOLVER_TOLERANCE;
long double m = (c - b)/2.0L;
if (absl(m) < tol || gb == 0.0L) return b;
// See if a bisection is forced
if (absl(e) < tol || absl(ga) <= absl(gb)) {
d = e = m; // Yes, it is
} else {
long double s = gb/ga;
long double p, q;
if (a == c) {
// Linear interpolation
p = 2.0L * m * s;
q = 1.0L - s;
} else {
// Inverse quadratic interpolation
long double z = ga/gc, r = gb/gc;
p = s * (2.0L*m*z*(z-r) - (b - a)*(r - 1.0L));
q = (z - 1.0L) * (r - 1.0L) * (s - 1.0L);
}
if (p > 0.0L) { q = -q; } else { p = -p; }
s = e; e = d;
if ((2.0L*p < 3.0L*m*q - absl(tol*q)) && (p < absl(0.5L*s*q))) {
d = p/q;
} else {
d = e = m;
}
}
a = b; ga = gb;
b = b + (absl(d) > tol ? d : (m > 0.0L ? tol : -tol));
gb = g(b, f, p1, p2);
if ((gb > 0.0L) == (gc > 0.0L)) break; // That is, goto "int:"
// Otherwise, goto "ext:"
}
}
}
// This routine accepts a discrete probability mass function P (represented as an array),
// a second array A, and an integer N that indicates their common length.
// It computes two outputs: a table of probability thresholds (returned in P) and
// a table of forwarding indices (returned in A).
// These tables are suitable for use with Walker's alias algorithm for sampling from
// the originally specified discrete probability mass function.
// For the original description of Walker's alias method, see:
// Alastair J. Walker. 1977. An efficient method for generating discrete random
// variables with general distributions. ACM Trans. Math. Software 3, 3
// (September 1977), 253-256. DOI: https://doi.org/10.1145/355744.355749
// However, his original version of the routine for building the tables runs in O(N**2) time.
// Following McFarland, we use a variant technique that is O(N), as described by Smith:
// Warren D. Smith. 2002. How to sample from a probability distribution.
// Unpublished. http://scorevoting.net/WarrenSmithPages/homepage/sampling.ps
void build_sampler(long double *P, int *A, int N) {
long double *X = malloc((N+1)*sizeof(long double));
int *B = malloc((N+1)*sizeof(int));
// First step: normalize the given probability distribution and scale by N.
long double sum = 0.0L;
for (int k = 0; k < N; k++) sum += P[k];
for (int k = 0; k < N; k++) P[k] = (P[k] / sum) * N;
// Copy P into X, and add a sentinel value.
for (int k = 0; k < N; k++) X[k] = P[k];
X[N] = 2.0L; // sentinel value
// A will become the table of forwarding indices.
// B will eventually describe a permutation on X such that every element less than 1.0
// has a lower index than any element that is not less than 1.0.
// Initally each is the identity map (element k contains the value k).
for (int k = 0; k < N; k++) A[k] = k;
for (int k = 0; k < N+1; k++) B[k] = k;
// This next step is reminiscent of a Quicksort partition: i and j are two fingers
// moving toward each other from opposite ends of X, and when i lands on an element
// not less than 1.0 and j lands on an element less than 1.0, they are _logically_
// swapped, not by updating X, but by updating the permutation in B.
int i = 0;
int j = N;
for (;;) {
while (X[B[i]] < 1.0L) i += 1;
while (X[B[j]] >= 1.0L) j -= 1;
if (i >= j) break;
int temp = B[i]; B[i] = B[j]; B[j] = temp;
}
i = j;
j += 1;
// At this point, X[B[k]] < 1.0L for all k <= i, and X[B[k]] >= 1.0L for all k >= j == i+1.
// This invariant will be maintained by the next loop, which moves i back out to the left
// and j back out to the right.
while (i >= 0) {
while (X[B[j]] <= 1.0L) j += 1;
if (j >= N) break;
// At this point, X[B[i]] is "overfunded" and X[B[j]] is "underfunded".
// During the sampling process, if the randomly chosen value in [0,1) is not
// less than X[B[i]], it will be construed as a choice of B[j] rather than of j.
// This is indicated by storing B[j] in A[B[i]]. In addition, X[B[j]] is updated
// to reflect that fact that X[B[i]] has "funded" 1-X[B[i]] of its desired
// probability mass.
A[B[i]] = B[j];
X[B[j]] -= (1.0L - X[B[i]]);
// It may be that the "generosity" of X[B[i]] has caused X[B[j]] to become overfunded.
// In that case, the two can be swapped (and j is incremented so that the former X[B[i]],
// now become X[B[j]], will not be further examined). Otherwise, i is decremented.
// In either case, i will then indicate a new overfunded slot to be considered.
if (X[B[j]] < 1.0L) {
int temp = B[i]; B[i] = B[j]; B[j] = temp;
j += 1;
} else {
i -= 1;
}
}
// All done! Now a sanity check.
long double *Q = malloc(N*sizeof(long double));
for (int k = 0; k < N; k++) Q[k] = X[k];
for (int k = 0; k < N; k++) Q[A[k]] += (1.0L - X[k]);
for (int k = 0; k < N; k++) check_equal(Q[k], P[k]);
// Copy the result table in X back out into the argument P.
for (int k = 0; k < N; k++) P[k] = X[k];
free(Q); free(B); free(X);
}
// The function that describes the exponential distribution with mean 1.
// See https://en.wikipedia.org/wiki/Exponential_distribution
long double exponential_f(long double x) {
return expl(-x);
}
// The cumulative distribution function for the exponential distribution with mean 1.
long double exponential_cdf(long double x) {
return 1.0L - expl(-x);
}
// The function that describes the normal distribution with mean 0 and standard deviation 1, scaled by sqrtl(0.5L*PI).
// See https://en.wikipedia.org/wiki/Normal_distribution
long double normal_f(long double x) {
return expl(-0.5L*x*x);
}
// The cumulative distribution function for the (right half of the) normal distribution with mean 0 and standard deviation 1.
long double normal_cdf(long double x) {
return sqrtl(0.5L*PI) * erfl(sqrtl(0.5L)*x);
}
// A function that will be zero at an x such that the new box will have area box_area.
long double box_g(long double x, longdoublefn f, long double last_Y_i, long double box_area) {
return x*(f(x) - last_Y_i) - box_area;
}
// A function that will be zero at an x such that, if f is normal_f, the tangent at point (x, f(x)) has slope m.
long double normal_tangent_g(long double x, longdoublefn f, long double m, long double unused) {
return x*f(x) - m;
}
// This routine generates all the parameters and tables for one kind of sampler.
void generate_tables(char *kind) {
// kind may be "normal" or "exponential"
assert(!strcmp(kind, "exponential") || !strcmp(kind, "normal"));
// SIZE must be a power of 2 (the code for Walker's alias method depends on it)
assert((SIZE & -SIZE) == SIZE);
// We require that SIZE <= 2048 because one place in the algorithm uses the
// high 53 bits of a randomly chosen 64-bit integer to make a floating-point
// (double) value after having already used the low bits to choose an integer
// in the range [0,SIZE), and it is important that these two values be independent.
// One consequence is that a value less than SIZE will certainly fit in a short
// (and we will use a byte instead if SIZE <= 256).
assert(SIZE <= 2048);
// A number of parameters need to be declared and then filled in according to the kind.
// The total area under the probability curve for x >= 0:
long double total_area_under_curve;
// The function for the probability curve and also its cumulative distribution function:
longdoublefn f, cdf;
// Heuristic initial bounds for using the solver to calculate the X values:
long double initial_lower_bound, initial_upper_bound;
if (!strcmp(kind, "exponential")) {
printf(" // Implementation support for modified-ziggurat implementation of nextExponential()\n\n");
total_area_under_curve = 1.0L;
f = exponential_f; cdf = exponential_cdf;
initial_lower_bound = 1.0L; initial_upper_bound = 10.0L;
} else if (!strcmp(kind, "normal")) {
printf(" // Implementation support for modified-ziggurat implementation of nextGaussian()\n\n");
// The "total area under curve" is for x >= 0 only, so we divide sqrtl(2.0L*PI) by 2.
total_area_under_curve = sqrtl(2.0L*PI)/2.0L;
f = normal_f; cdf = normal_cdf;
initial_lower_bound = 1.0L; initial_upper_bound = 4.0L;
}
// Make sure the claimed area under the curve is correct
// (or think of it as a sanity check on the cdf).
check_equal(total_area_under_curve, cdf(INFINITY) - cdf(0.0L));
// The first task is to compute the boxes of the modified ziggurat.
// The X values are found by an iterative solving process; after that the Y values are easy.
long double X[SIZE], Y[SIZE];
long double box_area = total_area_under_curve / ((long double)SIZE);
long double lower_bound = initial_lower_bound;
long double upper_bound = initial_upper_bound;
long double last_Y_i = 0.0L;
int i = 0;
while(lower_bound * f(0.0L) > box_area) {
// There are two solutions for X_i (a tall-skinny box and a long-flat box).
// We want the latter, so lower_bound is reduced gradually to avoid solving
// for the tall-skinny box. The two values of 0.9L are purely heuristic.
X[i] = fsolve(box_g, f, last_Y_i, box_area, lower_bound, upper_bound);
if (isnan(X[i])) {
lower_bound *= 0.9L;
} else {
last_Y_i = f(X[i]);
upper_bound = X[i];
lower_bound = 0.9L*X[i];
++i;
}
}
int number_of_layers = i;
// One _could_ think of there being an extra layer at the top with a box of width 0.
// However, to be consistent with McFarland's description, we will not call that a layer.
// Also, what McFarland calls an "overhanging box", we will call a "rectangle";
// each rectangle contains part of the curve, and the rest of the curve is above the tail.
// So there are number_of_layers boxes, numbered from 0 through (number_of_layers - 1);
// number_of_layers rectangles (one of which, the topmost, has no box to its left),
// numbered from 1 through number_of_layers; and a tail (which is to the right of box 0).
// For 1 <= k < number_of_layers, rectangle i is to the right of box i.
X[i] = 0.0L;
// We have all the X values; nocompute the corresponding Y values.
for (int k = 0; k < number_of_layers + 1; k++) Y[k] = f(X[k]);
// Now we have (number_of_layers + 1) X values and (number_of_layers + 1) Y values.
// For each i, 0 <= i <= number_of_layers, the point (X[i], Y[i]) lies on the curve.
// The next step is to compute the differences dX and dY.
long double dX[SIZE], dY[SIZE];
// Note that dX is calculated one way and dY the other way;
// that way all the difference values are positive.
for (int k = 0; k < number_of_layers; k++) dX[k] = X[k] - X[k+1];
for (int k = 0; k < number_of_layers; k++) dY[k] = Y[k+1] - Y[k];
// Sanity check to ensure all the boxes have the correct area
check_equal(X[0]*Y[0], box_area);
for (int k = 0; k < number_of_layers - 1; k++) check_equal(X[k+1]*dY[k], box_area);
// Now we can say that box i (0 <= i <= (number_of_layers - 1)) has width X[i] and height dY[i],
// and rectangle i (1 <= i <= number_of_layers) has width dX[i-1] and height dY[i-1].
// The next step is to construct a discrete probability distribution V
// that encompasses the tail and all the overhang areas (in the rectangles).
long double V[SIZE];
V[0] = cdf(INFINITY) - cdf(X[0]);
for (int k = 0; k < number_of_layers; k++) {
V[k+1] = (cdf(X[k]) - cdf(X[k+1])) - Y[k]*dX[k];
}
for (int k = number_of_layers + 1; k < SIZE; k++) V[k] = 0.0L;
// Now V[0] is the area of the tail, and V[i] (1 <= i <= number_of_layers)
// is the area within rectangle i that lies under the curve.
// Remaining entries are zero. (The only reason for this zero padding
// is to make the length of V be a power of 2, which allows generation
// of a randomly chosen index into V to be faster, using a masking operation
// rather than a modulus operator.)
// Sanity check that all area under the curve is accounted for.
long double V_sum = 0.0L;
for (int k = 0; k < number_of_layers + 1; k++) V_sum += V[k];
check_equal((double long)(SIZE - number_of_layers), V_sum/box_area);
// Report some interesting statistics.
printf(" // Fraction of the area under the curve that lies outside the layer boxes: %.4f\n", (double)(SIZE - number_of_layers)/(double)SIZE);
printf(" // Fraction of non-box area that lies in the tail of the distribution: %.4f\n", (double)(V[0]/V_sum));
printf("\n");
// Use V to construct tables called "alias_threshold" and "alias_map" for use with
// Walker's alias method for sampling a discrete distribution efficiently.
long double alias_threshold[SIZE];
int alias_map[SIZE];
// Routine build_sampler normalizes V and then turns it into thresholds,
// and also constructs the alias_map table.
build_sampler(V, alias_map, SIZE);
// Now produce the alias_threshold table from V by scaling it and converting to integer values.
// This is a trick that allows direct comparison with randomly chosen integer values,
// rather than requiring generation of a randomly chosen floating-point value.
for (int k = 0; k < SIZE; k++) {
if (V[k] >= 1.0L) {
// This "shouldn't happen", but rounding errors are possible, so we defend against it
alias_threshold[k] = max_int;
} else {
alias_threshold[k] = (int_type)(V[k] * max_uint - max_int);
}
}
// Here each m[k] is computed as a positive value, which is therefore the negative of the
// true slope of the diagonal line (within rectangle k+1) whose endpoints lie on the curve.
long double m[SIZE];
for (int k = 0; k < number_of_layers; k++) m[k] = dY[k]/dX[k];
// Now it is time to compute and output all the parameters.
// It is really important that each parameter be declared "final"; it allows
// a huge speed improvement because the Java compiler can then inline the constants.
printf(" static final int %sNumberOfLayers = %d;\n", kind, number_of_layers);
printf(" static final int %sLayerMask = 0x%x;\n", kind, SIZE-1);
printf(" static final int %sAliasMask = 0x%x;\n", kind, SIZE-1);
printf(" static final int %sSignCorrectionMask = 0x%x;\n", kind, (SIZE == 256) ? 0xff : 0xffffffff);
printf(" static final %s %sX0 = %19.17f;\n", java_float_type, kind, (double)(float_type)X[0]);
if (!strcmp(kind, "exponential")) {
// Within each rectangle, we want to find a point on the curve where the tangent
// is parallel to the diagonal line of the rectangle whose slope is m.
// The first derivative of the exponential function exp(-x) is -exp(-x), whose value
// at X[k] is -exp(-X[k]) which is -Y[k]. So we can compare m values and Y values directly.
// Sanity check: we expect Y[k+1] > m > Y[k].
for (int k = 0; k < number_of_layers; k++) {
assert(m[k] > Y[k]);
assert(Y[k+1] > m[k]);
}
// Now for some math. Within rectangle k+1, the point on the curve where the
// tangent is parallel to that diagonal must have coordinates (-log(m[k]), m[k]).
// The point on the diagonal directly above it (with the same x-coordinate) is
// (-log(m[k]), Y[k+1]-m[k]*(-log(m[k])-X[k+1])). The vertical distance between
// them is therefore Y[k+1] - m[k]*(-log(m[k])-X[k+1]) - m[k]. We can then divide
// this by dY[k] to normalize it to a fraction of the height of the rectangle.
// We could have a table of all these fractions, so that we would have just the
// right fraction for use with each rectangle; but it saves space (and loses very
// little time) to just compute the maximum such fraction over all rectangles,
// and then use that maximum fraction whenever processing any rectangle.
long double convex_margin = -INFINITY;
for (int k = 0; k < number_of_layers; k++) {
long double X_tangent = -logl(m[k]);
long double E = (Y[k+1] - m[k]*(X_tangent - X[k+1]) - m[k]) / dY[k];
convex_margin = (convex_margin > E) ? convex_margin : E;
}
int_type scaled_convex_margin = (int_type)(convex_margin * (long double)max_int);
printf(" static final %s %sConvexMargin = %lldL; // unscaled convex margin = %.4f\n",
java_int_type, kind, (long long)scaled_convex_margin, (double)convex_margin);
} else if (!strcmp(kind, "normal")) {
// Within each rectangle, we want to find a point on the curve where the tangent
// is parallel to the diagonal line of the rectangle whose slope is m.
long double inflection_point_x = 1.0L;
int normal_inflection_index = 0;
for (int k = 0; k < number_of_layers + 1; k++) {
if (X[k] > inflection_point_x) ++normal_inflection_index;
}
// The inflection point lies within rectangle normal_inflection_index.
// The x-coordinate of the inflection point lies between
// X[normal_inflection_index] and X[normal_inflection_index - 1].
// In principle we could have trouble if the inflection point lies exactly
// on corner of a box (but it doesn't happen in practice).
assert(X[normal_inflection_index] < inflection_point_x);
printf(" static final int normalInflectionIndex = %d;\n", normal_inflection_index);
// Now for some math. The first derivative of the normal curve function exp(-x*x/2)
// at X[k] is -X[k]*exp(-X[k]*X[k]/2) which is -X[k]*f(X[k]). We use the function
// normal_tangent_g with the solver to find the x-coordinate of a point on the
// curve within rectangle k+1 where the tangent has slope m[k]. The rectangle that
// contains the inflection point will have two such points, so that rectangle gets
// special processing.
// For each such tangent point, the idea is to compute the vertical distance between
// that point and the diagonal, then divide by the height of the rectangle to normalize.
// We could have a table of all these fractions, so that we would have just the
// right fraction(s) for use with each rectangle; but it saves space (and loses very
// little time) to just compute the maximum such fraction over a set of rectangles,
// and then conservatively use that maximum fraction whenever processing any rectangle.
// Instead of taking the maximum fraction over all rectangles (as we do for the
// exponential function) we compute two separate maxima: one over all tangent points
// below the diagonal (where the curve is convex) and one over all tangent points
// above the diagonal (where the curve is concave). Note that the rectangle containing
// the inflection point has one of each.
long double convex_margin = -INFINITY, concave_margin = -INFINITY;
for (int k = 0; k < number_of_layers; k++) {
// Process rectangle k+1
if ((k+1) <= normal_inflection_index) {
// The rectangle has a convex portion of the curve
long double lower_bound = ((k+1) == normal_inflection_index) ? inflection_point_x : X[k+1];
long double X_tangent = fsolve(normal_tangent_g, f, m[k], 0.0, lower_bound, X[k]);
long double E = (Y[k+1] - m[k]*(X_tangent - X[k+1]) - f(X_tangent)) / dY[k];
convex_margin = (convex_margin > E) ? convex_margin : E;
}
if ((k+1) >= normal_inflection_index) {
// The rectangle has a concave portion of the curve
long double upper_bound = ((k+1) == normal_inflection_index) ? inflection_point_x : X[k];
long double X_tangent = fsolve(normal_tangent_g, f, m[k], 0.0, X[k+1], upper_bound);
long double E = - (Y[k+1] - m[k]*(X_tangent - X[k+1]) - f(X_tangent)) / dY[k];
concave_margin = (concave_margin > E) ? concave_margin : E;
}
}
int_type scaled_convex_margin = (int_type)(convex_margin * (long double)max_int);
int_type scaled_concave_margin = (int_type)(concave_margin * (long double)max_int);
printf(" static final %s %sConvexMargin = %lldL; // unscaled convex margin = %.4f\n",
java_int_type, kind, (long long)scaled_convex_margin, (double)convex_margin);
printf(" static final %s %sConcaveMargin = %lldL; // unscaled concave margin = %.4f\n",
java_int_type, kind, (long long)scaled_concave_margin, (double)concave_margin);
}
printf("\n");
// Output the X array
printf(" // %s_X[i] = length of ziggurat layer i for %s distribution, scaled by 2**(-%d)\n", kind, kind, int_bits-1);
printf(" static final %s[] %sX = { // %d entries, which is %s_number_of_layers+1\n", java_float_type, kind, number_of_layers+1, kind);
for (int k = 0; k < number_of_layers+1; k++) {
if ((k & 0x3) == 0) printf(" ");
printf("%23.16e", (float_type)X[k] / (float_type)max_int);
if (k < number_of_layers) {
printf(",");
if ((k & 0x3) < 3) printf(" ");
else printf("\n");
} else {
printf(" };\n");
}
}
printf("\n");
// Output the Y array
printf(" // %s_Y[i] = value of the %s distribution function at %s_X[i], scaled by 2**(-%d)\n", kind, kind, kind, int_bits-1);
printf(" static final %s[] %sY = { // %d entries, which is %s_number_of_layers+1\n", java_float_type, kind, number_of_layers+1, kind);
for (int k = 0; k < number_of_layers+1; k++) {
if ((k & 0x3) == 0) printf(" ");
printf("%23.16e", (float_type)Y[k] / (float_type)max_int);
if (k < number_of_layers) {
printf(",");
if ((k & 0x3) < 3) printf(" ");
else printf("\n");
} else {
printf(" };\n");
}
}
printf("\n");
// Output the alias_threshold array
printf(" // alias_threshold[j] is a threshold for the probability mass function that has been\n");
printf(" // scaled by (2**%d - 1), translated by -(2**%d), and represented as a %s value;\n", int_bits, int_bits-1, java_int_type);
printf(" // in this way it can be directly compared to a randomly chosen %s value.\n", java_int_type);
printf(" static final long[] %sAliasThreshold = { // %d entries\n", kind, SIZE);
for (int k = 0; k < SIZE; k++) {
if ((k & 0x3) == 0) printf(" ");
printf("%20lldL", (long long)alias_threshold[k]);
if (k < (SIZE - 1)) {
printf(",");
if ((k & 0x3) < 3) printf(" ");
else printf("\n");
} else {
printf(" };\n");
}
}
printf("\n");
// Output the alias_map array
char *small_int_type = (SIZE <= 256) ? "byte" : "short";
int map_items_per_line = (SIZE == 256) ? 8 : 16;
printf(" static final %s[] %sAliasMap = { // %d entries\n", small_int_type, kind, SIZE);
for (int k = 0; k < SIZE; k++) {
if ((k % map_items_per_line) == 0) printf(" ");
if (SIZE == 256) printf("(byte)");
printf("%3d", alias_map[k]);
if (k < (SIZE - 1)) {
printf(",");
if ((k % map_items_per_line) < (map_items_per_line - 1)) printf(" ");
else printf("\n");
} else {
printf(" };\n");
}
}
printf("\n");
}
int main(int argc, char *argv[]) {
printf("// This Java source file is generated automatically by the program `create_ziggurat_tables.c`.\n");
printf("\n");
printf("/*\n");
printf(" * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.\n");
printf(" * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.\n");
printf(" *\n");
printf(" * This code is free software; you can redistribute it and/or modify it\n");
printf(" * under the terms of the GNU General Public License version 2 only, as\n");
printf(" * published by the Free Software Foundation. Oracle designates this\n");
printf(" * particular file as subject to the \"Classpath\" exception as provided\n");
printf(" * by Oracle in the LICENSE file that accompanied this code.\n");
printf(" *\n");
printf(" * This code is distributed in the hope that it will be useful, but WITHOUT\n");
printf(" * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n");
printf(" * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n");
printf(" * version 2 for more details (a copy is included in the LICENSE file that\n");
printf(" * accompanied this code).\n");
printf(" *\n");
printf(" * You should have received a copy of the GNU General Public License version\n");
printf(" * 2 along with this work; if not, write to the Free Software Foundation,\n");
printf(" * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.\n");
printf(" *\n");
printf(" * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA\n");
printf(" * or visit www.oracle.com if you need additional information or have any\n");
printf(" * questions.\n");
printf(" */\n");
printf("package java.util;\n");
printf("\n");
printf("class %sZigguratTables {\n", java_capitalized_float_type);
printf("\n");
generate_tables("exponential");
generate_tables("normal");
printf("}\n");
}