8279598: Provide adapter from RandomGenerator to Random

Reviewed-by: smarks, darcy
This commit is contained in:
Yasser Bazzi Bordonal 2022-05-04 17:55:50 +00:00 committed by Stuart Marks
parent 497a94fead
commit df8c2be5fe
3 changed files with 247 additions and 25 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -31,6 +31,7 @@ import java.util.random.RandomGenerator;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import jdk.internal.util.random.RandomSupport.*;
import static jdk.internal.util.random.RandomSupport.*;
@ -82,6 +83,164 @@ import jdk.internal.misc.Unsafe;
equidistribution = 0
)
public class Random implements RandomGenerator, java.io.Serializable {
/**
* Class used to wrap a {@link java.util.random.RandomGenerator} to
* {@link java.util.Random}.
*/
@SuppressWarnings("serial")
private static final class RandomWrapper extends Random implements RandomGenerator {
private final RandomGenerator generator;
//randomToWrap must never be null
private RandomWrapper(RandomGenerator randomToWrap) {
super(null);
this.generator = randomToWrap;
}
/**
* Throws {@code NotSerializableException}.
*
* @param s the object input stream
* @throws NotSerializableException always
*/
@Serial
private void readObject(ObjectInputStream s) throws NotSerializableException {
throw new NotSerializableException("not serializable");
}
/**
* Throws {@code NotSerializableException}.
*
* @param s the object output stream
* @throws NotSerializableException always
*/
@Serial
private void writeObject(ObjectOutputStream s) throws NotSerializableException {
throw new NotSerializableException("not serializable");
}
/**
* setSeed does not exist in {@link java.util.random.RandomGenerator} so can't
* use it.
*/
@Override
public void setSeed(long seed) {
throw new UnsupportedOperationException();
}
@Override
public void nextBytes(byte[] bytes) {
this.generator.nextBytes(bytes);
}
@Override
public int nextInt() {
return this.generator.nextInt();
}
@Override
public int nextInt(int bound) {
return this.generator.nextInt(bound);
}
@Override
public long nextLong() {
return this.generator.nextLong();
}
@Override
public boolean nextBoolean() {
return this.generator.nextBoolean();
}
@Override
public float nextFloat() {
return this.generator.nextFloat();
}
@Override
public double nextDouble() {
return this.generator.nextDouble();
}
@Override
public double nextGaussian() {
return this.generator.nextGaussian();
}
@Override
public IntStream ints(long streamSize) {
return this.generator.ints(streamSize);
}
@Override
public IntStream ints() {
return this.generator.ints();
}
@Override
public IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound) {
return this.generator.ints(streamSize, randomNumberOrigin, randomNumberBound);
}
@Override
public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
return this.generator.ints(randomNumberOrigin, randomNumberBound);
}
@Override
public LongStream longs(long streamSize) {
return this.generator.longs(streamSize);
}
@Override
public LongStream longs() {
return this.generator.longs();
}
@Override
public LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound) {
return this.generator.longs(streamSize, randomNumberOrigin, randomNumberBound);
}
@Override
public LongStream longs(long randomNumberOrigin, long randomNumberBound) {
return this.generator.longs(randomNumberOrigin, randomNumberBound);
}
@Override
public DoubleStream doubles(long streamSize) {
return this.generator.doubles(streamSize);
}
@Override
public DoubleStream doubles() {
return this.generator.doubles();
}
@Override
public DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound) {
return this.generator.doubles(streamSize, randomNumberOrigin, randomNumberBound);
}
@Override
public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
return this.generator.doubles(randomNumberOrigin, randomNumberBound);
}
//This method should never be reached unless done by reflection so we should throw when tried
@Override
protected int next(int bits) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return "RandomWrapper[" + generator + "]";
}
}
/** use serialVersionUID from JDK 1.1 for interoperability */
@java.io.Serial
static final long serialVersionUID = 3905348978240129619L;
@ -108,6 +267,10 @@ public class Random implements RandomGenerator, java.io.Serializable {
this(seedUniquifier() ^ System.nanoTime());
}
private Random(Void unused) {
this.seed = null;
}
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
@ -151,23 +314,40 @@ public class Random implements RandomGenerator, java.io.Serializable {
}
/**
* Sets the seed of this random number generator using a single
* {@code long} seed. The general contract of {@code setSeed} is
* that it alters the state of this random number generator object
* so as to be in exactly the same state as if it had just been
* created with the argument {@code seed} as a seed. The method
* {@code setSeed} is implemented by class {@code Random} by
* atomically updating the seed to
* Returns an instance of {@code Random} that delegates method calls to the {@link RandomGenerator}
* argument. If the generator is an instance of {@code Random}, it is returned. Otherwise, this method
* returns an instance of {@code Random} that delegates all methods except {@code setSeed} to the generator.
* The returned instance's {@code setSeed} method always throws {@link UnsupportedOperationException}.
* The returned instance is not serializable.
*
* @param generator the {@code RandomGenerator} calls are delegated to
* @return the delegating {@code Random} instance
* @throws NullPointerException if generator is null
* @since 19
*/
public static Random from(RandomGenerator generator) {
Objects.requireNonNull(generator);
if (generator instanceof Random rand)
return rand;
return new RandomWrapper(generator);
}
/**
* Sets or updates the seed of this random number generator using the
* provided {@code long} seed value (optional operation).
*
* @implSpec
* The implementation in this class alters the state of this random number
* generator so that it is in the same state as if it had just been created with
* {@link #Random(long) new Random(seed)}. It atomically updates the seed to
* <pre>{@code (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1)}</pre>
* and clearing the {@code haveNextNextGaussian} flag used by {@link
* #nextGaussian}.
* and clears the {@code haveNextNextGaussian} flag used by {@link #nextGaussian}.
* Note that this uses only 48 bits of the given seed value.
*
* <p>The implementation of {@code setSeed} by class {@code Random}
* happens to use only 48 bits of the given seed. In general, however,
* an overriding method may use all 64 bits of the {@code long}
* argument as a seed value.
*
* @param seed the initial seed
* @param seed the seed value
* @throws UnsupportedOperationException if the {@code setSeed}
* operation is not supported by this random number generator
*/
public synchronized void setSeed(long seed) {
this.seed.set(initialScramble(seed));
@ -175,21 +355,26 @@ public class Random implements RandomGenerator, java.io.Serializable {
}
/**
* Generates the next pseudorandom number. Subclasses should
* override this, as this is used by all other methods.
*
* <p>The general contract of {@code next} is that it returns an
* {@code int} value and if the argument {@code bits} is between
* Generates the next pseudorandom number. This method returns an
* {@code int} value such that, if the argument {@code bits} is between
* {@code 1} and {@code 32} (inclusive), then that many low-order
* bits of the returned value will be (approximately) independently
* chosen bit values, each of which is (approximately) equally
* likely to be {@code 0} or {@code 1}. The method {@code next} is
* implemented by class {@code Random} by atomically updating the seed to
* likely to be {@code 0} or {@code 1}.
*
* @apiNote
* The other random-producing methods in this class are implemented
* in terms of this method, so subclasses can override just this
* method to provide a different source of pseudorandom numbers for
* the entire class.
*
* @implSpec
* The implementation in this class atomically updates the seed to
* <pre>{@code (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)}</pre>
* and returning
* and returns
* <pre>{@code (int)(seed >>> (48 - bits))}.</pre>
*
* This is a linear congruential pseudorandom number generator, as
* <p>This is a linear congruential pseudorandom number generator, as
* defined by D. H. Lehmer and described by Donald E. Knuth in
* <cite>The Art of Computer Programming, Volume 2, Third edition:
* Seminumerical Algorithms</cite>, section 3.2.1.