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
@ -125,10 +132,11 @@ public class Random implements java.io.Serializable {
* 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:
* @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,8 +219,8 @@ 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:
* @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; )
@ -225,6 +233,7 @@ 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(),
@ -233,82 +242,6 @@ public class Random implements java.io.Serializable {
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,8 +249,8 @@ 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:
* @implSpec The method {@code nextInt} is
* implemented by class {@code Random} as if by:
* <pre>{@code
* public int nextInt() {
* return next(32);
@ -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,7 +271,9 @@ 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
* public int nextInt(int bound) {
@ -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,14 +336,13 @@ 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
* public long nextLong() {
@ -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,8 +369,8 @@ 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:
* @implSpec The method {@code nextBoolean} is implemented by class
* {@code Random} as if by:
* <pre>{@code
* public boolean nextBoolean() {
* return next(1) != 0;
@ -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:
* @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:
* @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,7 +480,7 @@ 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
* private double nextNextGaussian;
@ -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,11 +906,9 @@ 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();
}
/**
@ -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,8 +51,8 @@ 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
* SplittableRandom instance that shares no mutable state with the
@ -60,7 +60,7 @@ import java.util.stream.StreamSupport;
* 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);
}
/**
* 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);
return super.doubles(randomNumberOrigin, randomNumberBound);
}
}
}
/**
* 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");
}

View file

@ -0,0 +1,348 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "splittable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>1152</sup>. Class {@link L128X1024MixRandom} implements
* interfaces {@link RandomGenerator} and {@link SplittableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new split-off {@link L128X1024MixRandom}
* objects or streams of such objects.
*
* <p>The {@link L128X1024MixRandom} algorithm is a specific member of
* the LXM family of algorithms for pseudorandom number generators;
* for more information, see the documentation for package
* {@link jdk.random}. Each instance of {@link L128X1024MixRandom}
* has 1152 bits of state plus one 128-bit instance-specific parameter.
*
* <p>If two instances of {@link L128X1024MixRandom} are created with
* the same seed within the same program execution, and the same
* sequence of method calls is made for each, they will generate and
* return identical sequences of values.
*
* <p>As with {@link java.util.SplittableRandom}, instances of
* {@link L128X1024MixRandom} are <em>not</em> thread-safe. They are
* designed to be split, not shared, across threads (see the {@link #split}
* method). For example, a {@link java.util.concurrent.ForkJoinTask}
* fork/join-style computation using random numbers might include a
* construction of the form
* {@code new Subtask(someL128X1024MixRandom.split()).fork()}.
*
* <p>This class provides additional methods for generating random
* streams, that employ the above techniques when used in
* {@code stream.parallel()} mode.
*
* <p>Instances of {@link L128X1024MixRandom} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "L128X1024MixRandom",
group = "LXM",
i = 1024, j = 1, k = 128,
equidistribution = 1
)
public final class L128X1024MixRandom extends AbstractSplittableWithBrineGenerator {
/*
* Implementation Overview.
*
* The 128-bit parameter `a` is represented as two long fields `ah` and `al`.
* The 128-bit state variable `s` is represented as two long fields `sh` and `sl`.
*
* The split operation uses the current generator to choose 20
* new 64-bit long values that are then used to initialize the
* parameters `ah` and `al`, the state variables `sh`, `sl`,
* and the array `x` for a newly constructed generator.
*
* With extremely high probability, no two generators so chosen
* will have the same `a` parameter, and testing has indicated
* that the values generated by two instances of {@link L128X1024MixRandom}
* will be (approximately) independent if have different values for `a`.
*
* The default (no-argument) constructor, in essence, uses
* "defaultGen" to generate 20 new 64-bit values for the same
* purpose. Multiple generators created in this way will certainly
* differ in their `a` parameters. The defaultGen state must be accessed
* in a thread-safe manner, so we use an AtomicLong to represent
* this state. To bootstrap the defaultGen, we start off using a
* seed based on current time unless the
* java.util.secureRandomSeed property is set. This serves as a
* slimmed-down (and insecure) variant of SecureRandom that also
* avoids stalls that may occur when using /dev/random.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/*
* The length of the array x.
*/
private static final int N = 16;
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/*
* Low half of multiplier used in the LCG portion of the algorithm;
* the overall multiplier is (2**64 + ML).
* Chosen based on research by Sebastiano Vigna and Guy Steele (2019).
* The spectral scores for dimensions 2 through 8 for the multiplier 0x1d605bbb58c8abbfdLL
* are [0.991889, 0.907938, 0.830964, 0.837980, 0.780378, 0.797464, 0.761493].
*/
private static final long ML = 0xd605bbb58c8abbfdL;
/* ---------------- instance fields ---------------- */
/**
* The parameter that is used as an additive constant for the LCG.
* Must be odd (therefore al must be odd).
*/
private final long ah, al;
/**
* The per-instance state: sh and sl for the LCG; the array x for the xorshift;
* p is the rotating pointer into the array x.
* At least one of the 16 elements of the array x must be nonzero.
*/
private long sh, sl;
private final long[] x;
private int p = N - 1;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param ah high half of the additive parameter for the LCG
* @param al low half of the additive parameter for the LCG
* @param sh high half of the initial state for the LCG
* @param sl low half of the initial state for the LCG
* @param x0 first word of the initial state for the xorshift generator
* @param x1 second word of the initial state for the xorshift generator
* @param x2 third word of the initial state for the xorshift generator
* @param x3 fourth word of the initial state for the xorshift generator
* @param x4 fifth word of the initial state for the xorshift generator
* @param x5 sixth word of the initial state for the xorshift generator
* @param x6 seventh word of the initial state for the xorshift generator
* @param x7 eight word of the initial state for the xorshift generator
* @param x8 ninth word of the initial state for the xorshift generator
* @param x9 tenth word of the initial state for the xorshift generator
* @param x10 eleventh word of the initial state for the xorshift generator
* @param x11 twelfth word of the initial state for the xorshift generator
* @param x12 thirteenth word of the initial state for the xorshift generator
* @param x13 fourteenth word of the initial state for the xorshift generator
* @param x14 fifteenth word of the initial state for the xorshift generator
* @param x15 sixteenth word of the initial state for the xorshift generator
*/
public L128X1024MixRandom(long ah, long al, long sh, long sl,
long x0, long x1, long x2, long x3,
long x4, long x5, long x6, long x7,
long x8, long x9, long x10, long x11,
long x12, long x13, long x14, long x15) {
// Force a to be odd.
this.ah = ah;
this.al = al | 1;
this.sh = sh;
this.sl = sl;
this.x = new long[N];
this.x[0] = x0;
this.x[1] = x1;
this.x[2] = x2;
this.x[3] = x3;
this.x[4] = x4;
this.x[5] = x5;
this.x[6] = x6;
this.x[7] = x7;
this.x[8] = x8;
this.x[9] = x9;
this.x[10] = x10;
this.x[11] = x11;
this.x[12] = x12;
this.x[13] = x13;
this.x[14] = x14;
this.x[15] = x15;
// If x0, x1, ..., x15 are all zero (very unlikely), we must choose nonzero values.
if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15) == 0) {
long v = sh;
// At least fifteen of the sixteen values generated here will be nonzero.
for (int j = 0; j < N; j++) {
this.x[j] = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
}
}
}
/**
* Creates a new instance of {@link L128X1024MixRandom} using the
* specified {@code long} value as the initial seed. Instances of
* {@link L128X1024MixRandom} created with the same seed in the same
* program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L128X1024MixRandom(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The seed is hashed by mixMurmur64 to produce the `a` parameter.
// The seed is hashed by mixStafford13 to produce the initial `x[0]`,
// which will then be used to produce the first generated value.
// The other x values are filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and mixStafford13 as the mixer.
this(RandomSupport.mixMurmur64(seed ^= RandomSupport.SILVER_RATIO_64),
RandomSupport.mixMurmur64(seed += RandomSupport.GOLDEN_RATIO_64),
0,
1,
RandomSupport.mixStafford13(seed),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L128X1024MixRandom} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public L128X1024MixRandom() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L128X1024MixRandom} using the specified array of
* initial seed bytes. Instances of {@link L128X1024MixRandom} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L128X1024MixRandom(byte[] seed) {
// Convert the seed to 20 long values, of which the last 16 are not all zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 20, 16);
long ah = data[0], al = data[1], sh = data[2], sl = data[3];
// Force a to be odd.
this.ah = ah;
this.al = al | 1;
this.sh = sh;
this.sl = sl;
this.x = new long[N];
for (int j = 0; j < N; j++) {
this.x[j] = data[4+j];
}
}
/* ---------------- public methods ---------------- */
@Override
public SplittableGenerator split(SplittableGenerator source, long brine) {
// Pick a new instance "at random", but use the brine for (the low half of) `a`.
return new L128X1024MixRandom(source.nextLong(), brine << 1,
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong());
}
@Override
public long nextLong() {
// First part of xoroshiro1024: fetch array data
final int q = p;
final long s0 = x[p = (p + 1) & (N - 1)];
long s15 = x[q];
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = RandomSupport.mixLea64(sh + s0);
// Update the LCG subgenerator
// The LCG is, in effect, s = ((1LL << 64) + ML) * s + a, if only we had 128-bit arithmetic.
final long u = ML * sl;
// Note that Math.multiplyHigh computes the high half of the product of signed values,
// but what we need is the high half of the product of unsigned values; for this we use the
// formula "unsignedMultiplyHigh(a, b) = multiplyHigh(a, b) + ((a >> 63) & b) + ((b >> 63) & a)";
// in effect, each operand is added to the result iff the sign bit of the other operand is 1.
// (See Henry S. Warren, Jr., _Hacker's Delight_ (Second Edition), Addison-Wesley (2013),
// Section 8-3, p. 175; or see the First Edition, Addison-Wesley (2003), Section 8-3, p. 133.)
// If Math.unsignedMultiplyHigh(long, long) is ever implemented, the following line can become:
// sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
// and this entire comment can be deleted.
sh = (ML * sh) + (Math.multiplyHigh(ML, sl) + ((ML >> 63) & sl) + ((sl >> 63) & ML)) + sl + ah;
sl = u + al;
if (Long.compareUnsigned(sl, u) < 0) ++sh; // Handle the carry propagation from low half to high half.
// Second part of xoroshiro1024: update array data
s15 ^= s0;
x[q] = Long.rotateLeft(s0, 25) ^ s15 ^ (s15 << 27);
x[p] = Long.rotateLeft(s15, 36);
return result;
}
}

View file

@ -0,0 +1,277 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "splittable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>256</sup>. Class {@link L128X128MixRandom} implements
* interfaces {@link RandomGenerator} and {@link SplittableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new split-off {@link L128X128MixRandom}
* objects or streams of such objects.
*
* <p>The {@link L128X128MixRandom} algorithm is a specific member of
* the LXM family of algorithms for pseudorandom number generators;
* for more information, see the documentation for package
* {@link jdk.random}. Each instance of {@link L128X128MixRandom}
* has 256 bits of state plus one 128-bit instance-specific parameter.
*
* <p>If two instances of {@link L128X128MixRandom} are created with
* the same seed within the same program execution, and the same
* sequence of method calls is made for each, they will generate and
* return identical sequences of values.
*
* <p>As with {@link java.util.SplittableRandom}, instances of
* {@link L128X128MixRandom} are <em>not</em> thread-safe. They are
* designed to be split, not shared, across threads (see the {@link #split}
* method). For example, a {@link java.util.concurrent.ForkJoinTask}
* fork/join-style computation using random numbers might include a
* construction of the form
* {@code new Subtask(someL128X128MixRandom.split()).fork()}.
*
* <p>This class provides additional methods for generating random
* streams, that employ the above techniques when used in
* {@code stream.parallel()} mode.
*
* <p>Instances of {@link L128X128MixRandom} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "L128X128MixRandom",
group = "LXM",
i = 128, j = 1, k = 128,
equidistribution = 1
)
public final class L128X128MixRandom extends AbstractSplittableWithBrineGenerator {
/*
* Implementation Overview.
*
* The split operation uses the current generator to choose four new 64-bit
* long values that are then used to initialize the parameter `a` and the
* state variables `s`, `x0`, and `x1` for a newly constructed generator.
*
* With extremely high probability, no two generators so chosen
* will have the same `a` parameter, and testing has indicated
* that the values generated by two instances of {@link L128X128MixRandom}
* will be (approximately) independent if have different values for `a`.
*
* The default (no-argument) constructor, in essence, uses
* "defaultGen" to generate four new 64-bit values for the same
* purpose. Multiple generators created in this way will certainly
* differ in their `a` parameters. The defaultGen state must be accessed
* in a thread-safe manner, so we use an AtomicLong to represent
* this state. To bootstrap the defaultGen, we start off using a
* seed based on current time unless the
* java.util.secureRandomSeed property is set. This serves as a
* slimmed-down (and insecure) variant of SecureRandom that also
* avoids stalls that may occur when using /dev/random.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/*
* Low half of multiplier used in the LCG portion of the algorithm;
* the overall multiplier is (2**64 + ML).
* Chosen based on research by Sebastiano Vigna and Guy Steele (2019).
* The spectral scores for dimensions 2 through 8 for the multiplier 0x1d605bbb58c8abbfdLL
* are [0.991889, 0.907938, 0.830964, 0.837980, 0.780378, 0.797464, 0.761493].
*/
private static final long ML = 0xd605bbb58c8abbfdL;
/* ---------------- instance fields ---------------- */
/**
* The parameter that is used as an additive constant for the LCG.
* Must be odd (therefore al must be odd).
*/
private final long ah, al;
/**
* The per-instance state: sh and sl for the LCG; x0 and x1 for the xorshift.
* At least one of x0 and x1 must be nonzero.
*/
private long sh, sl, x0, x1;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param ah high half of the additive parameter for the LCG
* @param al low half of the additive parameter for the LCG
* @param sh high half of the initial state for the LCG
* @param sl low half of the initial state for the LCG
* @param x0 first word of the initial state for the xorshift generator
* @param x1 second word of the initial state for the xorshift generator
*/
public L128X128MixRandom(long ah, long al, long sh, long sl, long x0, long x1) {
// Force a to be odd.
this.ah = ah;
this.al = al | 1;
this.sh = sh;
this.sl = sl;
this.x0 = x0;
this.x1 = x1;
// If x0 and x1 are both zero, we must choose nonzero values.
if ((x0 | x1) == 0) {
long v = sh;
// At least one of the two values generated here will be nonzero.
this.x0 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x1 = RandomSupport.mixStafford13(v + RandomSupport.GOLDEN_RATIO_64);
}
}
/**
* Creates a new instance of {@link L128X128MixRandom} using the
* specified {@code long} value as the initial seed. Instances of
* {@link L128X128MixRandom} created with the same seed in the same
* program generate identical sequences of values.
*
* @param seed the initial seed
*/
public L128X128MixRandom(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The seed is hashed by mixMurmur64 to produce the `a` parameter.
// The seed is hashed by mixStafford13 to produce the initial `x0`,
// which will then be used to produce the first generated value.
// Then x1 is filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and mixStafford13 as the mixer.
this(RandomSupport.mixMurmur64(seed ^= RandomSupport.SILVER_RATIO_64),
RandomSupport.mixMurmur64(seed += RandomSupport.GOLDEN_RATIO_64),
0,
1,
RandomSupport.mixStafford13(seed),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L128X128MixRandom} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public L128X128MixRandom() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L128X128MixRandom} using the specified array of
* initial seed bytes. Instances of {@link L128X128MixRandom} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L128X128MixRandom(byte[] seed) {
// Convert the seed to 6 long values, of which the last 2 are not all zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 6, 2);
long ah = data[0], al = data[1], sh = data[2], sl = data[3], x0 = data[4], x1 = data[5];
// Force a to be odd.
this.ah = ah;
this.al = al | 1;
this.sh = sh;
this.sl = sl;
this.x0 = x0;
this.x1 = x1;
}
/* ---------------- public methods ---------------- */
@Override
public SplittableGenerator split(SplittableGenerator source, long brine) {
// Pick a new instance "at random", but use the brine for (the low half of) `a`.
return new L128X128MixRandom(source.nextLong(), brine << 1,
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong());
}
@Override
public long nextLong() {
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = RandomSupport.mixLea64(sh + x0);
// Update the LCG subgenerator
// The LCG is, in effect, s = ((1LL << 64) + ML) * s + a, if only we had 128-bit arithmetic.
final long u = ML * sl;
// Note that Math.multiplyHigh computes the high half of the product of signed values,
// but what we need is the high half of the product of unsigned values; for this we use the
// formula "unsignedMultiplyHigh(a, b) = multiplyHigh(a, b) + ((a >> 63) & b) + ((b >> 63) & a)";
// in effect, each operand is added to the result iff the sign bit of the other operand is 1.
// (See Henry S. Warren, Jr., _Hacker's Delight_ (Second Edition), Addison-Wesley (2013),
// Section 8-3, p. 175; or see the First Edition, Addison-Wesley (2003), Section 8-3, p. 133.)
// If Math.unsignedMultiplyHigh(long, long) is ever implemented, the following line can become:
// sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
// and this entire comment can be deleted.
sh = (ML * sh) + (Math.multiplyHigh(ML, sl) + ((ML >> 63) & sl) + ((sl >> 63) & ML)) + sl + ah;
sl = u + al;
if (Long.compareUnsigned(sl, u) < 0) ++sh; // Handle the carry propagation from low half to high half.
long q0 = x0, q1 = x1;
// Update the Xorshift subgenerator
{ // xoroshiro128v1_0
q1 ^= q0;
q0 = Long.rotateLeft(q0, 24);
q0 = q0 ^ q1 ^ (q1 << 16);
q1 = Long.rotateLeft(q1, 37);
}
x0 = q0; x1 = q1;
return result;
}
}

View file

@ -0,0 +1,301 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "splittable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>384</sup>. Class {@link L128X256MixRandom} implements
* interfaces {@link RandomGenerator} and {@link SplittableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new split-off {@link L128X256MixRandom}
* objects or streams of such objects.
*
* <p>The {@link L128X256MixRandom} algorithm is a specific member of
* the LXM family of algorithms for pseudorandom number generators;
* for more information, see the documentation for package
* {@link jdk.random}. Each instance of {@link L128X256MixRandom}
* has 384 bits of state plus one 128-bit instance-specific parameter.
*
* <p>If two instances of {@link L128X256MixRandom} are created with
* the same seed within the same program execution, and the same
* sequence of method calls is made for each, they will generate and
* return identical sequences of values.
*
* <p>As with {@link java.util.SplittableRandom}, instances of
* {@link L128X256MixRandom} are <em>not</em> thread-safe. They are
* designed to be split, not shared, across threads (see the {@link #split}
* method). For example, a {@link java.util.concurrent.ForkJoinTask}
* fork/join-style computation using random numbers might include a
* construction of the form
* {@code new Subtask(someL128X256MixRandom.split()).fork()}.
*
* <p>This class provides additional methods for generating random
* streams, that employ the above techniques when used in
* {@code stream.parallel()} mode.
*
* <p>Instances of {@link L128X256MixRandom} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "L128X256MixRandom",
group = "LXM",
i = 256, j = 1, k = 128,
equidistribution = 1
)
public final class L128X256MixRandom extends AbstractSplittableWithBrineGenerator {
/*
* Implementation Overview.
*
* The 128-bit parameter `a` is represented as two long fields `ah` and `al`.
* The 128-bit state variable `s` is represented as two long fields `sh` and `sl`.
*
* The split operation uses the current generator to choose eight
* new 64-bit long values that are then used to initialize the
* parameters `ah` and `al` and the state variables `sh`, `sl`,
* `x0`, `x1`, `x2`, and `x3` for a newly constructed generator.
*
* With extremely high probability, no two generators so chosen
* will have the same `a` parameter, and testing has indicated
* that the values generated by two instances of {@link L128X256MixRandom}
* will be (approximately) independent if have different values for `a`.
*
* The default (no-argument) constructor, in essence, uses
* "defaultGen" to generate eight new 64-bit values for the same
* purpose. Multiple generators created in this way will certainly
* differ in their `a` parameters. The defaultGen state must be accessed
* in a thread-safe manner, so we use an AtomicLong to represent
* this state. To bootstrap the defaultGen, we start off using a
* seed based on current time unless the
* java.util.secureRandomSeed property is set. This serves as a
* slimmed-down (and insecure) variant of SecureRandom that also
* avoids stalls that may occur when using /dev/random.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/*
* The equidistribution of the algorithm.
*/
private static final int EQUIDISTRIBUTION = 1;
/*
* Low half of multiplier used in the LCG portion of the algorithm;
* the overall multiplier is (2**64 + ML).
* Chosen based on research by Sebastiano Vigna and Guy Steele (2019).
* The spectral scores for dimensions 2 through 8 for the multiplier 0x1d605bbb58c8abbfdLL
* are [0.991889, 0.907938, 0.830964, 0.837980, 0.780378, 0.797464, 0.761493].
*/
private static final long ML = 0xd605bbb58c8abbfdL;
/* ---------------- instance fields ---------------- */
/**
* The parameter that is used as an additive constant for the LCG.
* Must be odd (therefore al must be odd).
*/
private final long ah, al;
/**
* The per-instance state: sh and sl for the LCG; x0, x1, x2, and x3 for the xorshift.
* At least one of the four fields x0, x1, x2, and x3 must be nonzero.
*/
private long sh, sl, x0, x1, x2, x3;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param ah high half of the additive parameter for the LCG
* @param al low half of the additive parameter for the LCG
* @param sh high half of the initial state for the LCG
* @param sl low half of the initial state for the LCG
* @param x0 first word of the initial state for the xorshift generator
* @param x1 second word of the initial state for the xorshift generator
* @param x2 third word of the initial state for the xorshift generator
* @param x3 fourth word of the initial state for the xorshift generator
*/
public L128X256MixRandom(long ah, long al, long sh, long sl, long x0, long x1, long x2, long x3) {
// Force a to be odd.
this.ah = ah;
this.al = al | 1;
this.sh = sh;
this.sl = sl;
this.x0 = x0;
this.x1 = x1;
this.x2 = x2;
this.x3 = x3;
// If x0, x1, x2, and x3 are all zero, we must choose nonzero values.
if ((x0 | x1 | x2 | x3) == 0) {
long v = sh;
// At least three of the four values generated here will be nonzero.
this.x0 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x1 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x2 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x3 = RandomSupport.mixStafford13(v + RandomSupport.GOLDEN_RATIO_64);
}
}
/**
* Creates a new instance of {@link L128X256MixRandom} using the
* specified {@code long} value as the initial seed. Instances of
* {@link L128X256MixRandom} created with the same seed in the same
* program generate identical sequences of values.
*
* @param seed the initial seed
*/
public L128X256MixRandom(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The seed is hashed by mixMurmur64 to produce the `a` parameter.
// The seed is hashed by mixStafford13 to produce the initial `x0`,
// which will then be used to produce the first generated value.
// The other x values are filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and mixStafford13 as the mixer.
this(RandomSupport.mixMurmur64(seed ^= RandomSupport.SILVER_RATIO_64),
RandomSupport.mixMurmur64(seed += RandomSupport.GOLDEN_RATIO_64),
0,
1,
RandomSupport.mixStafford13(seed),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L128X256MixRandom} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public L128X256MixRandom() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L128X256MixRandom} using the specified array of
* initial seed bytes. Instances of {@link L128X256MixRandom} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L128X256MixRandom(byte[] seed) {
// Convert the seed to 6 long values, of which the last 4 are not all zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 6, 4);
long ah = data[0], al = data[1], sh = data[2], sl = data[3],
x0 = data[4], x1 = data[5], x2 = data[6], x3 = data[7];
// Force a to be odd.
this.ah = ah;
this.al = al | 1;
this.sh = sh;
this.sl = sl;
this.x0 = x0;
this.x1 = x1;
this.x2 = x2;
this.x3 = x3;
}
/* ---------------- public methods ---------------- */
@Override
public SplittableGenerator split(SplittableGenerator source, long brine) {
// Pick a new instance "at random", but use the brine for (the low half of) `a`.
return new L128X256MixRandom(source.nextLong(), brine << 1,
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong());
}
@Override
public long nextLong() {
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = RandomSupport.mixLea64(sh + x0);
// Update the LCG subgenerator
// The LCG is, in effect, s = ((1LL << 64) + ML) * s + a, if only we had 128-bit arithmetic.
final long u = ML * sl;
// Note that Math.multiplyHigh computes the high half of the product of signed values,
// but what we need is the high half of the product of unsigned values; for this we use the
// formula "unsignedMultiplyHigh(a, b) = multiplyHigh(a, b) + ((a >> 63) & b) + ((b >> 63) & a)";
// in effect, each operand is added to the result iff the sign bit of the other operand is 1.
// (See Henry S. Warren, Jr., _Hacker's Delight_ (Second Edition), Addison-Wesley (2013),
// Section 8-3, p. 175; or see the First Edition, Addison-Wesley (2003), Section 8-3, p. 133.)
// If Math.unsignedMultiplyHigh(long, long) is ever implemented, the following line can become:
// sh = (ML * sh) + Math.unsignedMultiplyHigh(ML, sl) + sl + ah;
// and this entire comment can be deleted.
sh = (ML * sh) + (Math.multiplyHigh(ML, sl) + ((ML >> 63) & sl) + ((sl >> 63) & ML)) + sl + ah;
sl = u + al;
if (Long.compareUnsigned(sl, u) < 0) ++sh; // Handle the carry propagation from low half to high half.
// Update the Xorshift subgenerator
long q0 = x0, q1 = x1, q2 = x2, q3 = x3;
{ // xoshiro256 1.0
long t = q1 << 17;
q2 ^= q0;
q3 ^= q1;
q1 ^= q2;
q0 ^= q3;
q2 ^= t;
q3 = Long.rotateLeft(q3, 45);
}
x0 = q0; x1 = q1; x2 = q2; x3 = q3;
return result;
}
}

View file

@ -0,0 +1,256 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "splittable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>96</sup>. Class {@link L32X64MixRandom} implements
* interfaces {@link RandomGenerator} and {@link SplittableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new split-off {@link L32X64MixRandom}
* objects or streams of such objects.
*
* <p>The {@link L32X64MixRandom} algorithm is a specific member of
* the LXM family of algorithms for pseudorandom number generators;
* for more information, see the documentation for package
* {@link jdk.random}. Each instance of {@link L32X64MixRandom}
* has 96 bits of state plus one 32-bit instance-specific parameter.
*
* <p>If two instances of {@link L32X64MixRandom} are created with
* the same seed within the same program execution, and the same
* sequence of method calls is made for each, they will generate and
* return identical sequences of values.
*
* <p>As with {@link java.util.SplittableRandom}, instances of
* {@link L32X64MixRandom} are <em>not</em> thread-safe. They are
* designed to be split, not shared, across threads (see the {@link #split}
* method). For example, a {@link java.util.concurrent.ForkJoinTask}
* fork/join-style computation using random numbers might include a
* construction of the form
* {@code new Subtask(someL32X64MixRandom.split()).fork()}.
*
* <p>This class provides additional methods for generating random
* streams, that employ the above techniques when used in
* {@code stream.parallel()} mode.
*
* <p>Instances of {@link L32X64MixRandom} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "L32X64MixRandom",
group = "LXM",
i = 64, j = 1, k = 32,
equidistribution = 1
)
public final class L32X64MixRandom extends AbstractSplittableWithBrineGenerator {
/*
* Implementation Overview.
*
* The split operation uses the current generator to choose four new 32-bit
* int values that are then used to initialize the parameter `a` and the
* state variables `s`, `x0`, and `x1` for a newly constructed generator.
*
* With high probability, no two generators so chosen will have the same
* `a` parameter, and testing has indicated that the values generated by
* two instances of {@link L32X64MixRandom} will be (approximately)
* independent if the two instances have different values for `a`.
*
* The default (no-argument) constructor, in essence, uses
* "defaultGen" to generate four new 32-bit values for the same
* purpose. Multiple generators created in this way will certainly
* differ in their `a` parameters. The defaultGen state must be accessed
* in a thread-safe manner, so we use an AtomicLong to represent
* this state. To bootstrap the defaultGen, we start off using a
* seed based on current time unless the
* java.util.secureRandomSeed property is set. This serves as a
* slimmed-down (and insecure) variant of SecureRandom that also
* avoids stalls that may occur when using /dev/random.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/*
* Multiplier used in the LCG portion of the algorithm.
* Chosen based on research by Sebastiano Vigna and Guy Steele (2019).
* The spectral scores for dimensions 2 through 8 for the multiplier 0xadb4a92d
* are [0.975884, 0.936244, 0.755793, 0.877642, 0.751300, 0.789333, 0.728869].
*/
private static final int M = 0xadb4a92d;
/* ---------------- instance fields ---------------- */
/**
* The parameter that is used as an additive constant for the LCG.
* Must be odd.
*/
private final int a;
/**
* The per-instance state: s for the LCG; x0 and x1 for the xorshift.
* At least one of x0 and x1 must be nonzero.
*/
private int s, x0, x1;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param a additive parameter for the LCG
* @param s initial state for the LCG
* @param x0 first word of the initial state for the xorshift generator
* @param x1 second word of the initial state for the xorshift generator
*/
public L32X64MixRandom(int a, int s, int x0, int x1) {
// Force a to be odd.
this.a = a | 1;
this.s = s;
// If x0 and x1 are both zero, we must choose nonzero values.
if ((x0 | x1) == 0) {
int v = s;
// At least one of the two values generated here will be nonzero.
this.x0 = RandomSupport.mixMurmur32(v += RandomSupport.GOLDEN_RATIO_32);
this.x1 = RandomSupport.mixMurmur32(v + RandomSupport.GOLDEN_RATIO_32);
}
}
/**
* Creates a new instance of {@link L32X64MixRandom} using the
* specified {@code long} value as the initial seed. Instances of
* {@link L32X64MixRandom} created with the same seed in the same
* program generate identical sequences of values.
*
* @param seed the initial seed
*/
public L32X64MixRandom(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The high half of the seed is hashed by mixMurmur32 to produce the `a` parameter.
// The low half of the seed is hashed by mixLea32 to produce the initial `x0`,
// which will then be used to produce the first generated value.
// Then x1 is filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_32 as the gamma value and mixLea32 as the mixer.
this(RandomSupport.mixMurmur32((int)((seed ^= RandomSupport.SILVER_RATIO_64) >>> 32)),
1,
RandomSupport.mixLea32((int)(seed)),
RandomSupport.mixLea32((int)(seed) + RandomSupport.GOLDEN_RATIO_32));
}
/**
* Creates a new instance of {@link L32X64MixRandom} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public L32X64MixRandom() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L32X64MixRandom} using the specified array of
* initial seed bytes. Instances of {@link L32X64MixRandom} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L32X64MixRandom(byte[] seed) {
// Convert the seed to 4 int values, of which the last 2 are not all zero.
int[] data = RandomSupport.convertSeedBytesToInts(seed, 4, 2);
int a = data[0], s = data[1], x0 = data[2], x1 = data[3];
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x0 = x0;
this.x1 = x1;
}
/* ---------------- public methods ---------------- */
@Override
public SplittableGenerator split(SplittableGenerator source, long brine) {
// Pick a new instance "at random", but use (the low 31 bits of) the brine for `a`.
return new L32X64MixRandom((int)brine << 1, source.nextInt(),
source.nextInt(), source.nextInt());
}
@Override
public int nextInt() {
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final int result = RandomSupport.mixLea32(s + x0);
// Update the LCG subgenerator
s = M * s + a;
// Update the Xorshift subgenerator
int q0 = x0, q1 = x1;
{ // xoroshiro64
q1 ^= q0;
q0 = Integer.rotateLeft(q0, 26);
q0 = q0 ^ q1 ^ (q1 << 9);
q1 = Integer.rotateLeft(q1, 13);
}
x0 = q0; x1 = q1;
return result;
}
@Override
public long nextLong() {
return ((long)nextInt() << 32) ^ (long)nextInt();
}
}

View file

@ -0,0 +1,321 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "splittable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>1088</sup>. Class {@link L64X1024MixRandom} implements
* interfaces {@link RandomGenerator} and {@link SplittableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new split-off {@link L64X1024MixRandom}
* objects or streams of such objects.
*
* <p>The {@link L64X1024MixRandom} algorithm is a specific member of
* the LXM family of algorithms for pseudorandom number generators;
* for more information, see the documentation for package
* {@link jdk.random}. Each instance of {@link L64X1024MixRandom}
* has 1088 bits of state plus one 64-bit instance-specific parameter.
*
* <p>If two instances of {@link L64X1024MixRandom} are created with
* the same seed within the same program execution, and the same
* sequence of method calls is made for each, they will generate and
* return identical sequences of values.
*
* <p>As with {@link java.util.SplittableRandom}, instances of
* {@link L64X1024MixRandom} are <em>not</em> thread-safe. They are
* designed to be split, not shared, across threads (see the {@link #split}
* method). For example, a {@link java.util.concurrent.ForkJoinTask}
* fork/join-style computation using random numbers might include a
* construction of the form
* {@code new Subtask(someL64X1024MixRandom.split()).fork()}.
*
* <p>This class provides additional methods for generating random
* streams, that employ the above techniques when used in
* {@code stream.parallel()} mode.
*
* <p>Instances of {@link L64X1024MixRandom} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "L64X1024MixRandom",
group = "LXM",
i = 1024, j = 1, k = 64,
equidistribution = 16
)
public final class L64X1024MixRandom extends AbstractSplittableWithBrineGenerator {
/*
* Implementation Overview.
*
* The split() operation uses the current generator to choose 18 new 64-bit
* long values that are then used to initialize the parameter `a`, the
* state variable `s`, and the array `x` for a newly constructed generator.
*
* With extremely high probability, no two generators so chosen
* will have the same `a` parameter, and testing has indicated
* that the values generated by two instances of {@link L64X1024MixRandom}
* will be (approximately) independent if have different values for `a`.
*
* The default (no-argument) constructor, in essence, uses
* "defaultGen" to generate 18 new 64-bit values for the same
* purpose. Multiple generators created in this way will certainly
* differ in their `a` parameters. The defaultGen state must be accessed
* in a thread-safe manner, so we use an AtomicLong to represent
* this state. To bootstrap the defaultGen, we start off using a
* seed based on current time unless the
* java.util.secureRandomSeed property is set. This serves as a
* slimmed-down (and insecure) variant of SecureRandom that also
* avoids stalls that may occur when using /dev/random.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/*
* The length of the array x.
*/
private static final int N = 16;
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/*
* Multiplier used in the LCG portion of the algorithm.
* Chosen based on research by Sebastiano Vigna and Guy Steele (2019).
* The spectral scores for dimensions 2 through 8 for the multiplier 0xd1342543de82ef95
* are [0.958602, 0.937479, 0.870757, 0.822326, 0.820405, 0.813065, 0.760215].
*/
private static final long M = 0xd1342543de82ef95L;
/* ---------------- instance fields ---------------- */
/**
* The parameter that is used as an additive constant for the LCG.
* Must be odd.
*/
private final long a;
/**
* The per-instance state: s for the LCG; the array x for the xorshift;
* p is the rotating pointer into the array x.
* At least one of the 16 elements of the array x must be nonzero.
*/
private long s;
private final long[] x;
private int p = N - 1;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param a additive parameter for the LCG
* @param s initial state for the LCG
* @param x0 first word of the initial state for the xorshift generator
* @param x1 second word of the initial state for the xorshift generator
* @param x2 third word of the initial state for the xorshift generator
* @param x3 fourth word of the initial state for the xorshift generator
* @param x4 fifth word of the initial state for the xorshift generator
* @param x5 sixth word of the initial state for the xorshift generator
* @param x6 seventh word of the initial state for the xorshift generator
* @param x7 eight word of the initial state for the xorshift generator
* @param x8 ninth word of the initial state for the xorshift generator
* @param x9 tenth word of the initial state for the xorshift generator
* @param x10 eleventh word of the initial state for the xorshift generator
* @param x11 twelfth word of the initial state for the xorshift generator
* @param x12 thirteenth word of the initial state for the xorshift generator
* @param x13 fourteenth word of the initial state for the xorshift generator
* @param x14 fifteenth word of the initial state for the xorshift generator
* @param x15 sixteenth word of the initial state for the xorshift generator
*/
public L64X1024MixRandom(long a, long s,
long x0, long x1, long x2, long x3,
long x4, long x5, long x6, long x7,
long x8, long x9, long x10, long x11,
long x12, long x13, long x14, long x15) {
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x = new long[N];
this.x[0] = x0;
this.x[1] = x1;
this.x[2] = x2;
this.x[3] = x3;
this.x[4] = x4;
this.x[5] = x5;
this.x[6] = x6;
this.x[7] = x7;
this.x[8] = x8;
this.x[9] = x9;
this.x[10] = x10;
this.x[11] = x11;
this.x[12] = x12;
this.x[13] = x13;
this.x[14] = x14;
this.x[15] = x15;
// If x0, x1, ..., x15 are all zero (very unlikely), we must choose nonzero values.
if ((x0 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15) == 0) {
long v = s;
// At least fifteen of the sixteen values generated here will be nonzero.
for (int j = 0; j < N; j++) {
this.x[j] = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
}
}
}
/**
* Creates a new instance of {@link L64X1024MixRandom} using the
* specified {@code long} value as the initial seed. Instances of
* {@link L64X1024MixRandom} created with the same seed in the same
* program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L64X1024MixRandom(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The seed is hashed by mixMurmur64 to produce the `a` parameter.
// The seed is hashed by mixStafford13 to produce the initial `x[0]`,
// which will then be used to produce the first generated value.
// The other x values are filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and mixStafford13 as the mixer.
this(RandomSupport.mixMurmur64(seed ^= RandomSupport.SILVER_RATIO_64),
1,
RandomSupport.mixStafford13(seed),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L64X1024MixRandom} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public L64X1024MixRandom() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L64X1024MixRandom} using the specified array of
* initial seed bytes. Instances of {@link L64X1024MixRandom} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L64X1024MixRandom(byte[] seed) {
// Convert the seed to 18 long values, of which the last 16 are not all zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 18, 16);
long a = data[0], s = data[1];
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x = new long[N];
for (int j = 0; j < N; j++) {
this.x[j] = data[2+j];
}
}
/* ---------------- public methods ---------------- */
@Override
public SplittableGenerator split(SplittableGenerator source, long brine) {
// Pick a new instance "at random", but use the brine for `a`.
return new L64X1024MixRandom(brine << 1, source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong());
}
@Override
public long nextLong() {
// First part of xoroshiro1024: fetch array data
final int q = p;
final long s0 = x[p = (p + 1) & (N - 1)];
long s15 = x[q];
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = RandomSupport.mixLea64(s + s0);
// Update the LCG subgenerator
s = M * s + a; // LCG
// Second part of xoroshiro1024: update array data
s15 ^= s0;
x[q] = Long.rotateLeft(s0, 25) ^ s15 ^ (s15 << 27);
x[p] = Long.rotateLeft(s15, 36);
return result;
}
}

View file

@ -0,0 +1,254 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "splittable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>192</sup>. Class {@link L64X128MixRandom} implements
* interfaces {@link RandomGenerator} and {@link SplittableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new split-off {@link L64X128MixRandom}
* objects or streams of such objects.
*
* <p>The {@link L64X128MixRandom} algorithm is a specific member of
* the LXM family of algorithms for pseudorandom number generators;
* for more information, see the documentation for package
* {@link jdk.random}. Each instance of {@link L64X128MixRandom}
* has 192 bits of state plus one 64-bit instance-specific parameter.
*
* <p>If two instances of {@link L64X128MixRandom} are created with
* the same seed within the same program execution, and the same
* sequence of method calls is made for each, they will generate and
* return identical sequences of values.
*
* <p>As with {@link java.util.SplittableRandom}, instances of
* {@link L64X128MixRandom} are <em>not</em> thread-safe. They are
* designed to be split, not shared, across threads (see the {@link #split}
* method). For example, a {@link java.util.concurrent.ForkJoinTask}
* fork/join-style computation using random numbers might include a
* construction of the form
* {@code new Subtask(someL64X128MixRandom.split()).fork()}.
*
* <p>This class provides additional methods for generating random
* streams, that employ the above techniques when used in
* {@code stream.parallel()} mode.
*
* <p>Instances of {@link L64X128MixRandom} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "L64X128MixRandom",
group = "LXM",
i = 128, j = 1, k = 64,
equidistribution = 1
)
public final class L64X128MixRandom extends AbstractSplittableWithBrineGenerator {
/*
* Implementation Overview.
*
* The split operation uses the current generator to choose four new 64-bit
* long values that are then used to initialize the parameter `a` and the
* state variables `s`, `x0`, and `x1` for a newly constructed generator.
*
* With extremely high probability, no two generators so chosen
* will have the same `a` parameter, and testing has indicated
* that the values generated by two instances of {@link L64X128MixRandom}
* will be (approximately) independent if have different values for `a`.
*
* The default (no-argument) constructor, in essence, uses
* "defaultGen" to generate four new 64-bit values for the same
* purpose. Multiple generators created in this way will certainly
* differ in their `a` parameters. The defaultGen state must be accessed
* in a thread-safe manner, so we use an AtomicLong to represent
* this state. To bootstrap the defaultGen, we start off using a
* seed based on current time unless the
* java.util.secureRandomSeed property is set. This serves as a
* slimmed-down (and insecure) variant of SecureRandom that also
* avoids stalls that may occur when using /dev/random.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/*
* Multiplier used in the LCG portion of the algorithm.
* Chosen based on research by Sebastiano Vigna and Guy Steele (2019).
* The spectral scores for dimensions 2 through 8 for the multiplier 0xd1342543de82ef95L
* are [0.958602, 0.937479, 0.870757, 0.822326, 0.820405, 0.813065, 0.760215].
*/
private static final long M = 0xd1342543de82ef95L;
/* ---------------- instance fields ---------------- */
/**
* The parameter that is used as an additive constant for the LCG.
* Must be odd.
*/
private final long a;
/**
* The per-instance state: s for the LCG; x0 and x1 for the xorshift.
* At least one of x0 and x1 must be nonzero.
*/
private long s, x0, x1;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param a additive parameter for the LCG
* @param s initial state for the LCG
* @param x0 first word of the initial state for the xorshift generator
* @param x1 second word of the initial state for the xorshift generator
*/
public L64X128MixRandom(long a, long s, long x0, long x1) {
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x0 = x0;
this.x1 = x1;
// If x0 and x1 are both zero, we must choose nonzero values.
if ((x0 | x1) == 0) {
long v = s;
// At least one of the two values generated here will be nonzero.
this.x0 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x1 = RandomSupport.mixStafford13(v + RandomSupport.GOLDEN_RATIO_64);
}
}
/**
* Creates a new instance of {@link L64X128MixRandom} using the
* specified {@code long} value as the initial seed. Instances of
* {@link L64X128MixRandom} created with the same seed in the same
* program generate identical sequences of values.
*
* @param seed the initial seed
*/
public L64X128MixRandom(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The seed is hashed by mixMurmur64 to produce the `a` parameter.
// The seed is hashed by mixStafford13 to produce the initial `x0`,
// which will then be used to produce the first generated value.
// Then x1 is filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and mixStafford13 as the mixer.
this(RandomSupport.mixMurmur64(seed ^= RandomSupport.SILVER_RATIO_64),
1,
RandomSupport.mixStafford13(seed),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L64X128MixRandom} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public L64X128MixRandom() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L64X128MixRandom} using the specified array of
* initial seed bytes. Instances of {@link L64X128MixRandom} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L64X128MixRandom(byte[] seed) {
// Convert the seed to 4 long values, of which the last 2 are not all zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 4, 2);
long a = data[0], s = data[1], x0 = data[2], x1 = data[3];
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x0 = x0;
this.x1 = x1;
}
/* ---------------- public methods ---------------- */
@Override
public SplittableGenerator split(SplittableGenerator source, long brine) {
// Pick a new instance "at random", but use the brine for `a`.
return new L64X128MixRandom(brine << 1, source.nextLong(),
source.nextLong(), source.nextLong());
}
@Override
public long nextLong() {
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = RandomSupport.mixLea64(s + x0);
// Update the LCG subgenerator
s = M * s + a;
// Update the Xorshift subgenerator
long q0 = x0, q1 = x1;
{ // xoroshiro128v1_0
q1 ^= q0;
q0 = Long.rotateLeft(q0, 24);
q0 = q0 ^ q1 ^ (q1 << 16);
q1 = Long.rotateLeft(q1, 37);
}
x0 = q0; x1 = q1;
return result;
}
}

View file

@ -0,0 +1,254 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "splittable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>192</sup>. Class {@link L64X128StarStarRandom} implements
* interfaces {@link RandomGenerator} and {@link SplittableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new split-off {@link L64X128StarStarRandom}
* objects or streams of such objects.
*
* <p>The {@link L64X128StarStarRandom} algorithm is a specific member of
* the LXM family of algorithms for pseudorandom number generators;
* for more information, see the documentation for package
* {@link jdk.random}. Each instance of {@link L64X128StarStarRandom}
* has 192 bits of state plus one 64-bit instance-specific parameter.
*
* <p>If two instances of {@link L64X128StarStarRandom} are created with
* the same seed within the same program execution, and the same
* sequence of method calls is made for each, they will generate and
* return identical sequences of values.
*
* <p>As with {@link java.util.SplittableRandom}, instances of
* {@link L64X128StarStarRandom} are <em>not</em> thread-safe. They are
* designed to be split, not shared, across threads (see the {@link #split}
* method). For example, a {@link java.util.concurrent.ForkJoinTask}
* fork/join-style computation using random numbers might include a
* construction of the form
* {@code new Subtask(someL64X128StarStarRandom.split()).fork()}.
*
* <p>This class provides additional methods for generating random
* streams, that employ the above techniques when used in
* {@code stream.parallel()} mode.
*
* <p>Instances of {@link L64X128StarStarRandom} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "L64X128StarStarRandom",
group = "LXM",
i = 128, j = 1, k = 64,
equidistribution = 1
)
public final class L64X128StarStarRandom extends AbstractSplittableWithBrineGenerator {
/*
* Implementation Overview.
*
* The split operation uses the current generator to choose four new 64-bit
* long values that are then used to initialize the parameter `a` and the
* state variables `s`, `x0`, and `x1` for a newly constructed generator.
*
* With extremely high probability, no two generators so chosen
* will have the same `a` parameter, and testing has indicated
* that the values generated by two instances of {@link L64X128StarStarRandom}
* will be (approximately) independent if have different values for `a`.
*
* The default (no-argument) constructor, in essence, uses
* "defaultGen" to generate four new 64-bit values for the same
* purpose. Multiple generators created in this way will certainly
* differ in their `a` parameters. The defaultGen state must be accessed
* in a thread-safe manner, so we use an AtomicLong to represent
* this state. To bootstrap the defaultGen, we start off using a
* seed based on current time unless the
* java.util.secureRandomSeed property is set. This serves as a
* slimmed-down (and insecure) variant of SecureRandom that also
* avoids stalls that may occur when using /dev/random.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/*
* Multiplier used in the LCG portion of the algorithm.
* Chosen based on research by Sebastiano Vigna and Guy Steele (2019).
* The spectral scores for dimensions 2 through 8 for the multiplier 0xd1342543de82ef95
* are [0.958602, 0.937479, 0.870757, 0.822326, 0.820405, 0.813065, 0.760215].
*/
private static final long M = 0xd1342543de82ef95L;
/* ---------------- instance fields ---------------- */
/**
* The parameter that is used as an additive constant for the LCG.
* Must be odd.
*/
private final long a;
/**
* The per-instance state: s for the LCG; x0 and x1 for the xorshift.
* At least one of x0 and x1 must be nonzero.
*/
private long s, x0, x1;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param a additive parameter for the LCG
* @param s initial state for the LCG
* @param x0 first word of the initial state for the xorshift generator
* @param x1 second word of the initial state for the xorshift generator
*/
public L64X128StarStarRandom(long a, long s, long x0, long x1) {
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x0 = x0;
this.x1 = x1;
// If x0 and x1 are both zero, we must choose nonzero values.
if ((x0 | x1) == 0) {
long v = s;
// At least one of the two values generated here will be nonzero.
this.x0 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x1 = RandomSupport.mixStafford13(v + RandomSupport.GOLDEN_RATIO_64);
}
}
/**
* Creates a new instance of {@link L64X128StarStarRandom} using the
* specified {@code long} value as the initial seed. Instances of
* {@link L64X128StarStarRandom} created with the same seed in the same
* program generate identical sequences of values.
*
* @param seed the initial seed
*/
public L64X128StarStarRandom(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The seed is hashed by mixMurmur64 to produce the `a` parameter.
// The seed is hashed by mixStafford13 to produce the initial `x0`,
// which will then be used to produce the first generated value.
// Then x1 is filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and mixStafford13 as the mixer.
this(RandomSupport.mixMurmur64(seed ^= RandomSupport.SILVER_RATIO_64),
1,
RandomSupport.mixStafford13(seed),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L64X128StarStarRandom} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public L64X128StarStarRandom() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L64X128StarStarRandom} using the specified array of
* initial seed bytes. Instances of {@link L64X128StarStarRandom} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L64X128StarStarRandom(byte[] seed) {
// Convert the seed to 4 long values, of which the last 2 are not all zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 4, 2);
long a = data[0], s = data[1], x0 = data[2], x1 = data[3];
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x0 = x0;
this.x1 = x1;
}
/* ---------------- public methods ---------------- */
@Override
public SplittableGenerator split(SplittableGenerator source, long brine) {
// Pick a new instance "at random", but use the brine for `a`.
return new L64X128StarStarRandom(brine << 1, source.nextLong(),
source.nextLong(), source.nextLong());
}
@Override
public long nextLong() {
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = Long.rotateLeft((s + x0) * 5, 7) * 9; // "starstar" scrambler
// Update the LCG subgenerator
s = M * s + a;
// Update the Xorshift subgenerator
long q0 = x0, q1 = x1;
{ // xoroshiro128v1_0
q1 ^= q0;
q0 = Long.rotateLeft(q0, 24);
q0 = q0 ^ q1 ^ (q1 << 16);
q1 = Long.rotateLeft(q1, 37);
}
x0 = q0; x1 = q1;
return result;
}
}

View file

@ -0,0 +1,269 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.AbstractSplittableWithBrineGenerator;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "splittable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>320</sup>. Class {@link L64X256MixRandom} implements
* interfaces {@link RandomGenerator} and {@link SplittableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new split-off {@link L64X256MixRandom}
* objects or streams of such objects.
*
* <p>The {@link L64X256MixRandom} algorithm is a specific member of
* the LXM family of algorithms for pseudorandom number generators;
* for more information, see the documentation for package
* {@link jdk.random}. Each instance of {@link L64X256MixRandom}
* has 320 bits of state plus one 64-bit instance-specific parameter.
*
* <p>If two instances of {@link L64X256MixRandom} are created with
* the same seed within the same program execution, and the same
* sequence of method calls is made for each, they will generate and
* return identical sequences of values.
*
* <p>As with {@link java.util.SplittableRandom}, instances of
* {@link L64X256MixRandom} are <em>not</em> thread-safe. They are
* designed to be split, not shared, across threads (see the {@link #split}
* method). For example, a {@link java.util.concurrent.ForkJoinTask}
* fork/join-style computation using random numbers might include a
* construction of the form
* {@code new Subtask(someL64X256MixRandom.split()).fork()}.
*
* <p>This class provides additional methods for generating random
* streams, that employ the above techniques when used in
* {@code stream.parallel()} mode.
*
* <p>Instances of {@link L64X256MixRandom} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "L64X256MixRandom",
group = "LXM",
i = 256, j = 1, k = 64,
equidistribution = 4
)
public final class L64X256MixRandom extends AbstractSplittableWithBrineGenerator {
/*
* Implementation Overview.
*
* The split operation uses the current generator to choose six new 64-bit
* long values that are then used to initialize the parameter `a` and the
* state variables `s`, `x0`, `x1`, `x2`, and `x3` for a newly constructed
* generator.
*
* With extremely high probability, no two generators so chosen
* will have the same `a` parameter, and testing has indicated
* that the values generated by two instances of {@link L64X256MixRandom}
* will be (approximately) independent if have different values for `a`.
*
* The default (no-argument) constructor, in essence, uses
* "defaultGen" to generate six new 64-bit values for the same
* purpose. Multiple generators created in this way will certainly
* differ in their `a` parameters. The defaultGen state must be accessed
* in a thread-safe manner, so we use an AtomicLong to represent
* this state. To bootstrap the defaultGen, we start off using a
* seed based on current time unless the
* java.util.secureRandomSeed property is set. This serves as a
* slimmed-down (and insecure) variant of SecureRandom that also
* avoids stalls that may occur when using /dev/random.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/*
* Multiplier used in the LCG portion of the algorithm.
* Chosen based on research by Sebastiano Vigna and Guy Steele (2019).
* The spectral scores for dimensions 2 through 8 for the multiplier 0xd1342543de82ef95
* are [0.958602, 0.937479, 0.870757, 0.822326, 0.820405, 0.813065, 0.760215].
*/
private static final long M = 0xd1342543de82ef95L;
/* ---------------- instance fields ---------------- */
/**
* The parameter that is used as an additive constant for the LCG.
* Must be odd.
*/
private final long a;
/**
* The per-instance state: s for the LCG; x0, x1, x2, and x3 for the xorshift.
* At least one of the four fields x0, x1, x2, and x3 must be nonzero.
*/
private long s, x0, x1, x2, x3;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param a additive parameter for the LCG
* @param s initial state for the LCG
* @param x0 first word of the initial state for the xorshift generator
* @param x1 second word of the initial state for the xorshift generator
* @param x2 third word of the initial state for the xorshift generator
* @param x3 fourth word of the initial state for the xorshift generator
*/
public L64X256MixRandom(long a, long s, long x0, long x1, long x2, long x3) {
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x0 = x0;
this.x1 = x1;
this.x2 = x2;
this.x3 = x3;
// If x0, x1, x2, and x3 are all zero, we must choose nonzero values.
if ((x0 | x1 | x2 | x3) == 0) {
long v = s;
// At least three of the four values generated here will be nonzero.
this.x0 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x1 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x2 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64);
this.x3 = RandomSupport.mixStafford13(v + RandomSupport.GOLDEN_RATIO_64);
}
}
/**
* Creates a new instance of {@link L64X256MixRandom} using the
* specified {@code long} value as the initial seed. Instances of
* {@link L64X256MixRandom} created with the same seed in the same
* program generate identical sequences of values.
*
* @param seed the initial seed
*/
public L64X256MixRandom(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The seed is hashed by mixMurmur64 to produce the `a` parameter.
// The seed is hashed by mixStafford13 to produce the initial `x0`,
// which will then be used to produce the first generated value.
// The other x values are filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and mixStafford13 as the mixer.
this(RandomSupport.mixMurmur64(seed ^= RandomSupport.SILVER_RATIO_64),
1,
RandomSupport.mixStafford13(seed),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L64X256MixRandom} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public L64X256MixRandom() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link L64X256MixRandom} using the specified array of
* initial seed bytes. Instances of {@link L64X256MixRandom} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public L64X256MixRandom(byte[] seed) {
// Convert the seed to 6 long values, of which the last 4 are not all zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 6, 4);
long a = data[0], s = data[1], x0 = data[2], x1 = data[3], x2 = data[4], x3 = data[5];
// Force a to be odd.
this.a = a | 1;
this.s = s;
this.x0 = x0;
this.x1 = x1;
this.x2 = x2;
this.x3 = x3;
}
/* ---------------- public methods ---------------- */
@Override
public SplittableGenerator split(SplittableGenerator source, long brine) {
// Pick a new instance "at random", but use the brine for `a`.
return new L64X256MixRandom(brine << 1, source.nextLong(),
source.nextLong(), source.nextLong(),
source.nextLong(), source.nextLong());
}
@Override
public long nextLong() {
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = RandomSupport.mixLea64(s + x0);
// Update the LCG subgenerator
s = M * s + a;
// Update the Xorshift subgenerator
long q0 = x0, q1 = x1, q2 = x2, q3 = x3;
{ // xoshiro256 1.0
long t = q1 << 17;
q2 ^= q0;
q3 ^= q1;
q1 ^= q2;
q0 ^= q3;
q2 ^= t;
q3 = Long.rotateLeft(q3, 45);
}
x0 = q0; x1 = q1; x2 = q2; x3 = q3;
return result;
}
}

View file

@ -0,0 +1,284 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import java.util.random.RandomGenerator.LeapableGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "jumpable and leapable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>128</sup>. Class {@link Xoroshiro128PlusPlus} implements
* interfaces {@link RandomGenerator} and {@link LeapableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* numbers of type {@code int}, {@code long}, {@code float}, and {@code double}
* as well as creating new {@link Xoroshiro128PlusPlus} objects
* by "jumping" or "leaping".
* <p>
* The class {@link Xoroshiro128PlusPlus} uses the {@code xoroshiro128} algorithm
* (parameters 49, 21, 28) with the "++" scrambler that computes
* {@code Long.rotateLeft(s0 + s1, 17) + s0}.
* Its state consists of two {@code long} fields {@code x0} and {@code x1},
* which can take on any values provided that they are not both zero.
* The period of this generator is 2<sup>128</sup>-1.
* <p>
* The 64-bit values produced by the {@code nextLong()} method are equidistributed.
* To be precise, over the course of the cycle of length 2<sup>128</sup>-1,
* each nonzero {@code long} value is generated 2<sup>64</sup> times,
* but the value 0 is generated only 2<sup>64</sup>-1 times.
* The values produced by the {@code nextInt()}, {@code nextFloat()}, and {@code nextDouble()}
* methods are likewise equidistributed.
* <p>
* Instances {@link Xoroshiro128PlusPlus} are <em>not</em> thread-safe.
* They are designed to be used so that each thread as its own instance.
* The methods {@link #jump} and {@link #leap} and {@link #jumps} and {@link #leaps}
* can be used to construct new instances of {@link Xoroshiro128PlusPlus} that traverse
* other parts of the state cycle.
* <p>
* Instances of {@link Xoroshiro128PlusPlus} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "Xoroshiro128PlusPlus",
group = "Xoroshiro",
i = 128, j = 1, k = 0,
equidistribution = 2
)
public final class Xoroshiro128PlusPlus implements LeapableGenerator {
/*
* Implementation Overview.
*
* This is an implementation of the xoroshiro128++ algorithm version 1.0,
* written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org).
* See http://xoshiro.di.unimi.it and these two papers:
*
* Sebastiano Vigna. 2016. An Experimental Exploration of Marsaglia's
* xorshift Generators, Scrambled. ACM Transactions on Mathematical
* Software 42, 4, Article 30 (June 2016), 23 pages.
* https://doi.org/10.1145/2845077
*
* David Blackman and Sebastiano Vigna. 2018. Scrambled Linear
* Pseudorandom Number Generators. Computing Research Repository (CoRR).
* http://arxiv.org/abs/1805.01407
*
* The jump operation moves the current generator forward by 2*64
* steps; this has the same effect as calling nextLong() 2**64
* times, but is much faster. Similarly, the leap operation moves
* the current generator forward by 2*96 steps; this has the same
* effect as calling nextLong() 2**96 times, but is much faster.
* The copy method may be used to make a copy of the current
* generator. Thus one may repeatedly and cumulatively copy and
* jump to produce a sequence of generators whose states are well
* spaced apart along the overall state cycle (indeed, the jumps()
* and leaps() methods each produce a stream of such generators).
* The generators can then be parceled out to other threads.
*
* File organization: First the non-public methods that constitute the
* main algorithm, then the public methods. Note that many methods are
* defined by classes {@link AbstractJumpableGenerator} and {@link AbstractGenerator}.
*/
/* ---------------- static fields ---------------- */
/**
* Group name.
*/
private static final String GROUP = "Xoroshiro";
/**
* The seed generator for default constructors.
*/
private static final AtomicLong defaultGen = new AtomicLong(RandomSupport.initialSeed());
/* ---------------- instance fields ---------------- */
/**
* The per-instance state.
* At least one of the two fields x0 and x1 must be nonzero.
*/
private long x0, x1;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param x0 first word of the initial state
* @param x1 second word of the initial state
*/
public Xoroshiro128PlusPlus(long x0, long x1) {
this.x0 = x0;
this.x1 = x1;
// If x0 and x1 are both zero, we must choose nonzero values.
if ((x0 | x1) == 0) {
this.x0 = RandomSupport.GOLDEN_RATIO_64;
this.x1 = RandomSupport.SILVER_RATIO_64;
}
}
/**
* Creates a new instance of {@link Xoroshiro128PlusPlus} using the
* specified {@code long} value as the initial seed. Instances of
* {@link Xoroshiro128PlusPlus} created with the same seed in the same
* program generate identical sequences of values.
*
* @param seed the initial seed
*/
public Xoroshiro128PlusPlus(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The x values are then filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and Stafford13 as the mixer.
this(RandomSupport.mixStafford13(seed ^= RandomSupport.SILVER_RATIO_64),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link Xoroshiro128PlusPlus} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public Xoroshiro128PlusPlus() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(defaultGen.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link Xoroshiro128PlusPlus} using the specified array of
* initial seed bytes. Instances of {@link Xoroshiro128PlusPlus} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public Xoroshiro128PlusPlus(byte[] seed) {
// Convert the seed to 2 long values, which are not both zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 2, 2);
long x0 = data[0], x1 = data[1];
this.x0 = x0;
this.x1 = x1;
}
/* ---------------- public methods ---------------- */
public Xoroshiro128PlusPlus copy() {
return new Xoroshiro128PlusPlus(x0, x1);
}
/*
* The following two comments are quoted from http://prng.di.unimi.it/xoroshiro128plusplus.c
*/
/*
* To the extent possible under law, the author has dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty.
* <p>
* See http://creativecommons.org/publicdomain/zero/1.0/.
*/
/*
* This is xoroshiro128++ 1.0, one of our all-purpose, rock-solid,
* small-state generators. It is extremely (sub-ns) fast and it passes all
* tests we are aware of, but its state space is large enough only for
* mild parallelism.
* <p>
* For generating just floating-point numbers, xoroshiro128+ is even
* faster (but it has a very mild bias, see notes in the comments).
* <p>
* The state must be seeded so that it is not everywhere zero. If you have
* a 64-bit seed, we suggest to seed a splitmix64 generator and use its
* output to fill s.
*/
@Override
public long nextLong() {
final long s0 = x0;
long s1 = x1;
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = Long.rotateLeft(s0 + s1, 17) + s0; // "plusplus" scrambler
s1 ^= s0;
x0 = Long.rotateLeft(s0, 49) ^ s1 ^ (s1 << 21); // a, b
x1 = Long.rotateLeft(s1, 28); // c
return result;
}
@Override
public double jumpDistance() {
return 0x1.0p64;
}
@Override
public double leapDistance() {
return 0x1.0p96;
}
private static final long[] JUMP_TABLE = { 0x2bd7a6a6e99c2ddcL, 0x0992ccaf6a6fca05L };
private static final long[] LEAP_TABLE = { 0x360fd5f2cf8d5d99L, 0x9c6e6877736c46e3L };
@Override
public void jump() {
jumpAlgorithm(JUMP_TABLE);
}
@Override
public void leap() {
jumpAlgorithm(LEAP_TABLE);
}
private void jumpAlgorithm(long[] table) {
long s0 = 0, s1 = 0;
for (int i = 0; i < table.length; i++) {
for (int b = 0; b < 64; b++) {
if ((table[i] & (1L << b)) != 0) {
s0 ^= x0;
s1 ^= x1;
}
nextLong();
}
}
x0 = s0;
x1 = s1;
}
}

View file

@ -0,0 +1,315 @@
/*
* 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 jdk.random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.random.RandomGenerator;
import java.util.random.RandomGenerator.LeapableGenerator;
import jdk.internal.util.random.RandomSupport;
import jdk.internal.util.random.RandomSupport.RandomGeneratorProperties;
/**
* A "jumpable and leapable" pseudorandom number generator (PRNG) whose period
* is roughly 2<sup>256</sup>. Class {@link Xoshiro256PlusPlus} implements
* interfaces {@link RandomGenerator} and {@link LeapableGenerator},
* and therefore supports methods for producing pseudorandomly chosen
* values of type {@code int}, {@code long}, {@code float}, {@code double},
* and {@code boolean} (and for producing streams of pseudorandomly chosen
* numbers of type {@code int}, {@code long}, and {@code double}),
* as well as methods for creating new {@link Xoshiro256PlusPlus} objects
* by moving forward either a large distance (2<sup>128</sup>) or a very large
* distance (2<sup>192</sup>) around the state cycle.
* <p>
* Series of generated values pass the TestU01 BigCrush and PractRand test suites
* that measure independence and uniformity properties of random number generators.
* (Most recently validated with
* <a href="http://simul.iro.umontreal.ca/testu01/tu01.html">version 1.2.3 of TestU01</a>
* and <a href="http://pracrand.sourceforge.net">version 0.90 of PractRand</a>.
* Note that TestU01 BigCrush was used to test not only values produced by the {@code nextLong()}
* method but also the result of bit-reversing each value produced by {@code nextLong()}.)
* 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.
* <p>
* The class {@link Xoshiro256PlusPlus} uses the {@code xoshiro256} algorithm,
* version 1.0 (parameters 17, 45), with the "++" scrambler that computes
* {@code Long.rotateLeft(s0 + s3, 23) + s0}.
* Its state consists of four {@code long} fields {@code x0}, {@code x1}, {@code x2},
* and {@code x3}, which can take on any values provided that they are not all zero.
* The period of this generator is 2<sup>256</sup>-1.
* <p>
* The 64-bit values produced by the {@code nextLong()} method are equidistributed.
* To be precise, over the course of the cycle of length 2<sup>256</sup>-1,
* each nonzero {@code long} value is generated 2<sup>192</sup> times,
* but the value 0 is generated only 2<sup>192</sup>-1 times.
* The values produced by the {@code nextInt()}, {@code nextFloat()}, and {@code nextDouble()}
* methods are likewise equidistributed.
* Moreover, the 64-bit values produced by the {@code nextLong()} method are 3-equidistributed.
* <p>
* Instances {@link Xoshiro256PlusPlus} are <em>not</em> thread-safe.
* They are designed to be used so that each thread as its own instance.
* The methods {@link #jump} and {@link #leap} and {@link #jumps} and {@link #leaps}
* can be used to construct new instances of {@link Xoshiro256PlusPlus} that traverse
* other parts of the state cycle.
* <p>
* Instances of {@link Xoshiro256PlusPlus} are not cryptographically
* secure. Consider instead using {@link java.security.SecureRandom}
* in security-sensitive applications. Additionally,
* default-constructed instances do not use a cryptographically random
* seed unless the {@linkplain System#getProperty system property}
* {@code java.util.secureRandomSeed} is set to {@code true}.
*
* @since 17
*
*/
@RandomGeneratorProperties(
name = "Xoshiro256PlusPlus",
group = "Xoshiro",
i = 256, j = 1, k = 0,
equidistribution = 4
)
public final class Xoshiro256PlusPlus implements LeapableGenerator {
/*
* Implementation Overview.
*
* This is an implementation of the xoroshiro128++ algorithm version 1.0,
* written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org).
* See http://xoshiro.di.unimi.it and these two papers:
*
* Sebastiano Vigna. 2016. An Experimental Exploration of Marsaglia's
* xorshift Generators, Scrambled. ACM Transactions on Mathematical
* Software 42, 4, Article 30 (June 2016), 23 pages.
* https://doi.org/10.1145/2845077
*
* David Blackman and Sebastiano Vigna. 2018. Scrambled Linear
* Pseudorandom Number Generators. Computing Research Repository (CoRR).
* http://arxiv.org/abs/1805.01407
*
* The jump operation moves the current generator forward by 2*128
* steps; this has the same effect as calling nextLong() 2**128
* times, but is much faster. Similarly, the leap operation moves
* the current generator forward by 2*192 steps; this has the same
* effect as calling nextLong() 2**192 times, but is much faster.
* The copy method may be used to make a copy of the current
* generator. Thus one may repeatedly and cumulatively copy and
* jump to produce a sequence of generators whose states are well
* spaced apart along the overall state cycle (indeed, the jumps()
* and leaps() methods each produce a stream of such generators).
* The generators can then be parceled out to other threads.
*
* File organization: First static fields, then instance
* fields, then constructors, then instance methods.
*/
/* ---------------- static fields ---------------- */
/**
* The seed generator for default constructors.
*/
private static final AtomicLong DEFAULT_GEN = new AtomicLong(RandomSupport.initialSeed());
/* ---------------- instance fields ---------------- */
/**
* The per-instance state.
* At least one of the four fields x0, x1, x2, and x3 must be nonzero.
*/
private long x0, x1, x2, x3;
/* ---------------- constructors ---------------- */
/**
* Basic constructor that initializes all fields from parameters.
* It then adjusts the field values if necessary to ensure that
* all constraints on the values of fields are met.
*
* @param x0 first word of the initial state
* @param x1 second word of the initial state
* @param x2 third word of the initial state
* @param x3 fourth word of the initial state
*/
public Xoshiro256PlusPlus(long x0, long x1, long x2, long x3) {
this.x0 = x0;
this.x1 = x1;
this.x2 = x2;
this.x3 = x3;
// If x0, x1, x2, and x3 are all zero, we must choose nonzero values.
if ((x0 | x1 | x2 | x3) == 0) {
// At least three of the four values generated here will be nonzero.
this.x0 = RandomSupport.mixStafford13(x0 += RandomSupport.GOLDEN_RATIO_64);
this.x1 = (x0 += RandomSupport.GOLDEN_RATIO_64);
this.x2 = (x0 += RandomSupport.GOLDEN_RATIO_64);
this.x3 = (x0 += RandomSupport.GOLDEN_RATIO_64);
}
}
/**
* Creates a new instance of {@link Xoshiro256PlusPlus} using the
* specified {@code long} value as the initial seed. Instances of
* {@link Xoshiro256PlusPlus} created with the same seed in the same
* program generate identical sequences of values.
*
* @param seed the initial seed
*/
public Xoshiro256PlusPlus(long seed) {
// Using a value with irregularly spaced 1-bits to xor the seed
// argument tends to improve "pedestrian" seeds such as 0 or
// other small integers. We may as well use SILVER_RATIO_64.
//
// The x values are then filled in as if by a SplitMix PRNG with
// GOLDEN_RATIO_64 as the gamma value and Stafford13 as the mixer.
this(RandomSupport.mixStafford13(seed ^= RandomSupport.SILVER_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed += RandomSupport.GOLDEN_RATIO_64),
RandomSupport.mixStafford13(seed + RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link Xoshiro256PlusPlus} that is likely to
* generate sequences of values that are statistically independent
* of those of any other instances in the current program execution,
* but may, and typically does, vary across program invocations.
*/
public Xoshiro256PlusPlus() {
// Using GOLDEN_RATIO_64 here gives us a good Weyl sequence of values.
this(DEFAULT_GEN.getAndAdd(RandomSupport.GOLDEN_RATIO_64));
}
/**
* Creates a new instance of {@link Xoshiro256PlusPlus} using the specified array of
* initial seed bytes. Instances of {@link Xoshiro256PlusPlus} created with the same
* seed array in the same program execution generate identical sequences of values.
*
* @param seed the initial seed
*/
public Xoshiro256PlusPlus(byte[] seed) {
// Convert the seed to 4 long values, which are not all zero.
long[] data = RandomSupport.convertSeedBytesToLongs(seed, 4, 4);
long x0 = data[0], x1 = data[1], x2 = data[2], x3 = data[3];
this.x0 = x0;
this.x1 = x1;
this.x2 = x2;
this.x3 = x3;
}
/* ---------------- public methods ---------------- */
public Xoshiro256PlusPlus copy() {
return new Xoshiro256PlusPlus(x0, x1, x2, x3);
}
/*
* The following two comments are quoted from http://prng.di.unimi.it/xoshiro256plusplus.c
*/
/*
* To the extent possible under law, the author has dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty.
* <p>
* See http://creativecommons.org/publicdomain/zero/1.0/.
*/
/*
* This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators.
* It has excellent (sub-ns) speed, a state (256 bits) that is large
* enough for any parallel application, and it passes all tests we are
* aware of.
*
* For generating just floating-point numbers, xoshiro256+ is even faster.
*
* The state must be seeded so that it is not everywhere zero. If you have
* a 64-bit seed, we suggest to seed a splitmix64 generator and use its
* output to fill s.
*/
@Override
public long nextLong() {
// Compute the result based on current state information
// (this allows the computation to be overlapped with state update).
final long result = Long.rotateLeft(x0 + x3, 23) + x0; // "plusplus" scrambler
long q0 = x0, q1 = x1, q2 = x2, q3 = x3;
{ // xoshiro256 1.0
long t = q1 << 17;
q2 ^= q0;
q3 ^= q1;
q1 ^= q2;
q0 ^= q3;
q2 ^= t;
q3 = Long.rotateLeft(q3, 45);
}
x0 = q0; x1 = q1; x2 = q2; x3 = q3;
return result;
}
@Override
public double jumpDistance() {
return 0x1.0p128;
}
@Override
public double leapDistance() {
return 0x1.0p192;
}
private static final long[] JUMP_TABLE = {
0x180ec6d33cfd0abaL, 0xd5a61266f0c9392cL, 0xa9582618e03fc9aaL, 0x39abdc4529b1661cL };
private static final long[] LEAP_TABLE = {
0x76e15d3efefdcbbfL, 0xc5004e441c522fb3L, 0x77710069854ee241L, 0x39109bb02acbe635L };
@Override
public void jump() {
jumpAlgorithm(JUMP_TABLE);
}
@Override
public void leap() {
jumpAlgorithm(LEAP_TABLE);
}
private void jumpAlgorithm(long[] table) {
long s0 = 0, s1 = 0, s2 = 0, s3 = 0;
for (int i = 0; i < table.length; i++) {
for (int b = 0; b < 64; b++) {
if ((table[i] & (1L << b)) != 0) {
s0 ^= x0;
s1 ^= x1;
s2 ^= x2;
s3 ^= x3;
}
nextLong();
}
}
x0 = s0;
x1 = s1;
x2 = s2;
x3 = s3;
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2020, 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.
*/
import jdk.internal.util.random.RandomSupport;
/**
* Defines implementations of the
* {@linkplain java.util.random.RandomGenerator RandomGenerator Interface}.
*
* @provides jdk.random.L128X1024MixRandom
* @provides jdk.random.L128X128MixRandom
* @provides jdk.random.L128X256MixRandom
* @provides jdk.random.L32X64MixRandom
* @provides jdk.random.L64X1024MixRandom
* @provides jdk.random.L64X128MixRandom
* @provides jdk.random.L64X128StarStarRandom
* @provides jdk.random.L64X256MixRandom
* @provides jdk.random.Xoroshiro128PlusPlus
* @provides jdk.random.Xoshiro256PlusPlus
*
* @use java.util.random.RandomGenerator
* @use jdk.internal.util.random.RandomSupport
*
* @moduleGraph
* @since 16
*/
module jdk.random {
exports jdk.random to
java.base;
provides java.util.random.RandomGenerator with
jdk.random.L32X64MixRandom,
jdk.random.L64X128MixRandom,
jdk.random.L64X128StarStarRandom,
jdk.random.L64X256MixRandom,
jdk.random.L64X1024MixRandom,
jdk.random.L128X128MixRandom,
jdk.random.L128X256MixRandom,
jdk.random.L128X1024MixRandom,
jdk.random.Xoroshiro128PlusPlus,
jdk.random.Xoshiro256PlusPlus;
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,191 @@
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.concurrent.ThreadLocalRandom;
import java.util.random.RandomGenerator;
import java.util.random.RandomGenerator.ArbitrarilyJumpableGenerator;
import java.util.random.RandomGenerator.JumpableGenerator;
import java.util.random.RandomGenerator.LeapableGenerator;
import java.util.random.RandomGenerator.SplittableGenerator;
import java.util.random.RandomGenerator.StreamableGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
/**
* @test
* @summary Ensure that all implementations of RandomGenerator supply required methods.
* @bug 8248862
* @run main RandomTestCoverage
* @key randomness
*/
public class RandomTestCoverage {
static void coverRandomGenerator(RandomGenerator rng) {
boolean bool = rng.nextBoolean();
byte[] bytes = new byte[8];
rng.nextBytes(bytes);
int i1 = rng.nextInt();
int i2 = rng.nextInt(10);
int i3 = rng.nextInt(5, 10);
long l1 = rng.nextLong();
long l2 = rng.nextLong(10L);
long l3 = rng.nextLong(5L, 10L);
float f1 = rng.nextFloat();
float f2 = rng.nextFloat(1.0f);
float f3 = rng.nextFloat(0.5f, 1.0f);
double d1 = rng.nextDouble();
double d2 = rng.nextDouble(1.0);
double d3 = rng.nextDouble(0.5, 1.0);
double exp = rng.nextExponential();
double gauss1 = rng.nextGaussian();
double gauss2 = rng.nextGaussian(0.5, 2.0);
IntStream intStream1 = rng.ints();
IntStream intStream2 = rng.ints(5, 10);
IntStream intStream3 = rng.ints(5L);
IntStream intStream4 = rng.ints(5L, 5, 10);
LongStream longStream1 = rng.longs();
LongStream longStream2 = rng.longs(5L, 10L);
LongStream longStream3 = rng.longs(5L);
LongStream longStream4 = rng.longs(5L, 5L, 10L);
DoubleStream doubleStream1 = rng.doubles();
DoubleStream doubleStream2 = rng.doubles(0.5, 1.0);
DoubleStream doubleStream3 = rng.doubles(5);
DoubleStream doubleStream4 = rng.doubles(5, 0.5, 1.0);
}
static void checkPredicates(RandomGeneratorFactory factory) {
RandomGenerator rng = factory.create();
if (rng instanceof ArbitrarilyJumpableGenerator != factory.isArbitrarilyJumpable()) {
throw new RuntimeException("isArbitrarilyJumpable failing");
}
if (rng instanceof JumpableGenerator != factory.isJumpable()) {
throw new RuntimeException("isJumpable failing");
}
if (rng instanceof LeapableGenerator != factory.isLeapable()) {
throw new RuntimeException("isLeapable failing");
}
if (rng instanceof SplittableGenerator != factory.isSplittable()) {
throw new RuntimeException("isArbitrarilyJumpable failing");
}
if (rng instanceof StreamableGenerator != factory.isStreamable()) {
throw new RuntimeException("isArbitrarilyJumpable failing");
}
}
static void coverStreamable(StreamableGenerator rng) {
Stream<RandomGenerator> rngs1 = rng.rngs();
Stream<RandomGenerator> rngs2 = rng.rngs(5L);
}
static void coverSplittable(SplittableGenerator rng) {
SplittableGenerator s1 = rng.split();
SplittableGenerator s2 = rng.split(rng);
Stream<SplittableGenerator> s3 = rng.splits();
Stream<SplittableGenerator> s4 = rng.splits(5L);
Stream<SplittableGenerator> s5 = rng.splits(rng);
Stream<SplittableGenerator> s6 = rng.splits(5L, rng);
}
static void coverJumpable(JumpableGenerator rng) {
JumpableGenerator j1 = rng.copy();
rng.jump();
RandomGenerator j2 = rng.copyAndJump();
double d = rng.jumpDistance();
Stream<RandomGenerator> j3 = rng.jumps();
Stream<RandomGenerator> j4 = rng.jumps(5L);
}
static void coverLeapable(LeapableGenerator rng) {
LeapableGenerator l1 = rng.copy();
rng.leap();
JumpableGenerator l2 = rng.copyAndLeap();
double d = rng.leapDistance();
Stream<JumpableGenerator> l3 = rng.leaps();
Stream<JumpableGenerator> l4 = rng.leaps(5L);
}
static void coverArbitrarilyJumpable(ArbitrarilyJumpableGenerator rng) {
ArbitrarilyJumpableGenerator a1 = rng.copy();
rng.jump();
rng.leap();
rng.jump(1.2345);
rng.jumpPowerOfTwo(4);
RandomGenerator a2 = rng.copyAndJump();
RandomGenerator a3 = rng.copyAndJump(1.2345);
Stream<ArbitrarilyJumpableGenerator> a4 = rng.jumps(1.2345);
Stream<ArbitrarilyJumpableGenerator> a5 = rng.jumps(5L, 1.2345);
}
static void coverOf(String name) {
coverRandomGenerator(RandomGenerator.of(name));
coverFactory(RandomGeneratorFactory.of(name));
}
static void coverFactory(RandomGeneratorFactory factory) {
String name = factory.name();
String group = factory.group();
int stateBits = factory.stateBits();
int equidistribution = factory.equidistribution();
BigInteger period = factory.period();
boolean isStatistical = factory.isStatistical();
boolean isStochastic = factory.isStochastic();
boolean isHardware = factory.isHardware();
boolean isArbitrarilyJumpable = factory.isArbitrarilyJumpable();
boolean isJumpable = factory.isJumpable();
boolean isLeapable = factory.isLeapable();
boolean isSplittable = factory.isSplittable();
coverRandomGenerator(factory.create());
coverRandomGenerator(factory.create(12345L));
coverRandomGenerator(factory.create(new byte[] {1, 2, 3, 4, 5, 6, 7, 8}));
}
static void coverDefaults() {
RandomGeneratorFactory<RandomGenerator> factory =
RandomGeneratorFactory.getDefault();
RandomGenerator rng = RandomGenerator.getDefault();
}
public static void main(String[] args) throws Throwable {
RandomGeneratorFactory.all()
.forEach(factory -> {
coverFactory(factory);
coverOf(factory.name());
});
RandomGeneratorFactory.all(StreamableGenerator.class)
.forEach(factory -> {
coverStreamable(factory.create());
});
RandomGeneratorFactory.all(SplittableGenerator.class)
.forEach(factory -> {
coverSplittable(factory.create());
});
RandomGeneratorFactory.all(JumpableGenerator.class)
.forEach(factory -> {
coverJumpable(factory.create());
});
RandomGeneratorFactory.all(LeapableGenerator.class)
.forEach(factory -> {
coverLeapable(factory.create());
});
RandomGeneratorFactory.all(ArbitrarilyJumpableGenerator.class)
.forEach(factory -> {
coverArbitrarilyJumpable(factory.create());
});
coverRandomGenerator(new SecureRandom());
coverRandomGenerator(ThreadLocalRandom.current());
}
}

View file

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

View file

@ -39,8 +39,6 @@
*
*/
import java.util.Random;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.NotCompliantMBeanException;
@ -117,12 +115,12 @@ public class NotCompliantCauseTest {
}
public interface NotCompliantMXBean {
Random returnRandom();
Object returnObject();
}
public static class NotCompliant implements NotCompliantMXBean {
public Random returnRandom() {
return new Random();
public Object returnObject() {
return new Object();
}
}