package fj.test;
import fj.F;
import fj.data.Option;
import java.util.Random;
import static fj.data.Option.none;
import static fj.data.Option.some;
import static java.lang.Math.max;
import static java.lang.Math.min;
/**
* A random number generator.
*
* @version %build.number%
*/
public final class Rand {
private final F<Option<Long>, F<Integer, F<Integer, Integer>>> f;
private final F<Option<Long>, F<Double, F<Double, Double>>> g;
// TODO Change to F<Long,Rand> when rand(f,g) is removed
private final Option<F<Long, Rand>> optOnReseed;
private Rand(
F<Option<Long>, F<Integer, F<Integer, Integer>>> f,
F<Option<Long>, F<Double, F<Double, Double>>> g,
Option<F<Long, Rand>> optOnReseed) {
this.f = f;
this.g = g;
this.optOnReseed = optOnReseed;
}
/**
* Randomly chooses a value between the given range (inclusive).
*
* @param seed The seed to use for random generation.
* @param from The minimum value to choose.
* @param to The maximum value to choose.
* @return A random value in the given range.
*/
public int choose(final long seed, final int from, final int to) {
return f.f(some(seed)).f(from).f(to);
}
/**
* Randomly chooses a value between the given range (inclusive).
*
* @param from The minimum value to choose.
* @param to The maximum value to choose.
* @return A random value in the given range.
*/
public int choose(final int from, final int to) {
return f.f(Option.none()).f(from).f(to);
}
public long choose(final long from, final long to) {
return g.f(Option.none()).f((double) from).f((double) to).longValue();
}
/**
* Randomly chooses a value between the given range (inclusive).
*
* @param seed The seed to use for random generation.
* @param from The minimum value to choose.
* @param to The maximum value to choose.
* @return A random value in the given range.
*/
public double choose(final long seed, final double from, final double to) {
return g.f(some(seed)).f(from).f(to);
}
/**
* Randomly chooses a value between the given range (inclusive).
*
* @param from The minimum value to choose.
* @param to The maximum value to choose.
* @return A random value in the given range.
*/
public double choose(final double from, final double to) {
return g.f(Option.none()).f(from).f(to);
}
/**
* Gives this random generator a new seed.
*
* @param seed The seed of the new random generator.
* @return A random generator with the given seed.
*/
public Rand reseed(long seed) {
return optOnReseed.<Rand>option(
() -> {
throw new IllegalStateException("reseed() called on a Rand created with deprecated rand() method");
},
onReseed -> onReseed.f(seed));
}
/**
* Constructs a random generator from the given functions that supply a range to produce a
* result.
* <p>
* Calling {@link #reseed(long)} on an instance returned from this method will
* result in an exception being thrown.
*
* @deprecated As of release 4.6, use {@link #rand(F, F, F)}.
*
* @param f The integer random generator.
* @param g The floating-point random generator.
* @return A random generator from the given functions that supply a range to produce a result.
*/
// TODO Change Option<F<Long,Rand>> optOnReseed to F<Long,Road> onReseed when removing this method
@Deprecated
public static Rand rand(
F<Option<Long>, F<Integer, F<Integer, Integer>>> f,
F<Option<Long>, F<Double, F<Double, Double>>> g) {
return new Rand(f, g, none());
}
/**
* Constructs a reseedable random generator from the given functions that supply a range to produce a
* result.
*
* @param f The integer random generator.
* @param g The floating-point random generator.
* @param onReseed Function to create a reseeded Rand.
* @return A random generator from the given functions that supply a range to produce a result.
*/
public static Rand rand(
F<Option<Long>, F<Integer, F<Integer, Integer>>> f,
F<Option<Long>, F<Double, F<Double, Double>>> g,
F<Long, Rand> onReseed) {
return new Rand(f, g, some(onReseed));
}
/**
* A standard random generator that uses {@link Random}.
*/
public static final Rand standard = createStandard(new Random());
private static Rand createStandard(Random defaultRandom) {
return rand(
optSeed -> from -> to ->
standardChooseInt(optSeed.<Random>option(() -> defaultRandom, Random::new), from, to),
optSeed -> from -> to ->
standardChooseDbl(optSeed.<Random>option(() -> defaultRandom, Random::new), from, to),
newSeed -> createStandard(new Random(newSeed)));
}
/*
* Returns a uniformly distributed value between min(from,to) (inclusive) and max(from,to) (inclusive).
*/
private static int standardChooseInt(Random random, int from, int to) {
int result;
if (from != to) {
int min = min(from, to);
int max = max(from, to);
long range = (1L + max) - min;
long bound = Long.MAX_VALUE - (Long.MAX_VALUE % range);
long r = random.nextLong() & Long.MAX_VALUE;
while (r >= bound) {
// Ensure uniformity
r = random.nextLong() & Long.MAX_VALUE;
}
result = (int) ((r % range) + min);
} else {
result = from;
}
return result;
}
/*
* Returns a uniformly distributed value between min(from,to) (inclusive) and max(from,to) (exclusive)
*
* In theory, this differs from the choose() contract, which specifies a closed interval.
* In practice, the difference shouldn't matter.
*/
private static double standardChooseDbl(Random random, double from, double to) {
double min = min(from, to);
double max = max(from, to);
return ((max - min) * random.nextDouble()) + min;
}
}