classlib: update RandomGenerator implementation (#743)

This commit is contained in:
Ivan Hetman 2023-09-19 10:39:57 +03:00 committed by GitHub
parent ef818ac4c5
commit 4a081db1c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 503 additions and 412 deletions

View File

@ -0,0 +1,94 @@
/*
* Copyright 2023 ihromant.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.impl;
import java.util.function.DoubleSupplier;
public class RandomUtils {
public static void checkStreamSize(long streamSize) {
if (streamSize < 0L) {
throw new IllegalArgumentException();
}
}
public static void checkBound(float bound) {
if (!(bound > 0.0 && bound < Float.POSITIVE_INFINITY)) {
throw new IllegalArgumentException();
}
}
public static void checkBound(double bound) {
if (!(bound > 0.0 && bound < Double.POSITIVE_INFINITY)) {
throw new IllegalArgumentException();
}
}
public static void checkBound(int bound) {
if (bound <= 0) {
throw new IllegalArgumentException();
}
}
public static void checkBound(long bound) {
if (bound <= 0) {
throw new IllegalArgumentException();
}
}
public static void checkRange(float origin, float bound) {
if (!(origin < bound && bound - origin < Float.POSITIVE_INFINITY)) {
throw new IllegalArgumentException();
}
}
public static void checkRange(double origin, double bound) {
if (!(origin < bound && bound - origin < Double.POSITIVE_INFINITY)) {
throw new IllegalArgumentException();
}
}
public static void checkRange(int origin, int bound) {
if (origin >= bound) {
throw new IllegalArgumentException();
}
}
public static void checkRange(long origin, long bound) {
if (origin >= bound) {
throw new IllegalArgumentException();
}
}
public static double[] pairGaussian(DoubleSupplier rng) {
/*
* This implementation uses the polar method to generate two gaussian
* values at a time. One is returned, and the other is stored to be returned
* next time.
*/
double v1;
double v2;
double s;
do {
v1 = 2 * rng.getAsDouble() - 1;
v2 = 2 * rng.getAsDouble() - 1;
s = v1 * v1 + v2 * v2;
} while (s >= 1 || s == 0);
double m = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
return new double[] { v1 * m, v2 * m };
}
}

View File

@ -15,32 +15,23 @@
*/ */
package org.teavm.classlib.java.util; package org.teavm.classlib.java.util;
import java.util.function.DoublePredicate;
import java.util.function.IntPredicate;
import java.util.function.LongPredicate;
import org.teavm.backend.wasm.runtime.WasmSupport; import org.teavm.backend.wasm.runtime.WasmSupport;
import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.impl.RandomUtils;
import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.lang.TMath;
import org.teavm.classlib.java.lang.TObject; import org.teavm.classlib.java.lang.TObject;
import org.teavm.classlib.java.util.stream.TDoubleStream; import org.teavm.classlib.java.util.random.TRandomGenerator;
import org.teavm.classlib.java.util.stream.TIntStream;
import org.teavm.classlib.java.util.stream.TLongStream;
import org.teavm.classlib.java.util.stream.doubleimpl.TSimpleDoubleStreamImpl;
import org.teavm.classlib.java.util.stream.intimpl.TSimpleIntStreamImpl;
import org.teavm.classlib.java.util.stream.longimpl.TSimpleLongStreamImpl;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
public class TRandom extends TObject implements TSerializable { public class TRandom extends TObject implements TRandomGenerator, TSerializable {
/** A stored gaussian value for nextGaussian() */ /** A stored gaussian value for nextGaussian() */
private double storedGaussian; private double storedGaussian;
/** Whether storedGuassian value is valid */ /** Whether storedGuassian value is valid */
private boolean haveStoredGaussian; private boolean haveStoredGaussian;
public TRandom() { public TRandom() {
} }
@ -50,24 +41,12 @@ public class TRandom extends TObject implements TSerializable {
public void setSeed(@SuppressWarnings("unused") long seed) { public void setSeed(@SuppressWarnings("unused") long seed) {
} }
protected int next(int bits) { @Override
if (bits == 32) {
return (int) (nextDouble() * ((1L << 32) - 1) + Integer.MIN_VALUE);
} else {
return (int) (nextDouble() * (1L << TMath.min(32, bits)));
}
}
public void nextBytes(byte[] bytes) {
for (int i = 0; i < bytes.length; ++i) {
bytes[i] = (byte) next(8);
}
}
public int nextInt() { public int nextInt() {
return next(32); return (int) (0x1.0p+32 * nextDouble() + Integer.MIN_VALUE);
} }
@Override
public int nextInt(int n) { public int nextInt(int n) {
if (n <= 0) { if (n <= 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
@ -75,73 +54,17 @@ public class TRandom extends TObject implements TSerializable {
return (int) (nextDouble() * n); return (int) (nextDouble() * n);
} }
public int nextInt(int origin, int bound) { @Override
if (origin >= bound) {
throw new IllegalArgumentException();
}
int range = bound - origin;
if (range > 0) {
return nextInt(range) + origin;
} else {
while (true) {
int value = nextInt();
if (value >= origin && value < bound) {
return value;
}
}
}
}
public long nextLong() { public long nextLong() {
return ((long) nextInt() << 32) | nextInt(); return ((long) nextInt() << 32) | nextInt();
} }
public long nextLong(long bound) { @Override
if (bound <= 0) {
throw new IllegalArgumentException();
}
while (true) {
long value = nextLong();
long result = value % bound;
if (value - result + (bound - 1) < 0) {
return result;
}
}
}
public long nextLong(long origin, long bound) {
if (origin >= bound) {
throw new IllegalArgumentException();
}
long range = bound - origin;
if (range > 0) {
return nextLong(range) + origin;
} else {
while (true) {
long value = nextLong();
if (value >= origin && value < bound) {
return value;
}
}
}
}
public boolean nextBoolean() {
return nextInt() % 2 == 0;
}
public float nextFloat() { public float nextFloat() {
return (float) nextDouble(); return (float) nextDouble();
} }
public float nextFloat(float bound) { @Override
return (float) nextDouble(bound);
}
public float nextFloat(float origin, float bound) {
return (float) nextDouble(origin, bound);
}
public double nextDouble() { public double nextDouble() {
if (PlatformDetector.isC()) { if (PlatformDetector.isC()) {
return crand(); return crand();
@ -152,24 +75,6 @@ public class TRandom extends TObject implements TSerializable {
} }
} }
public double nextDouble(double bound) {
if (bound <= 0) {
throw new IllegalArgumentException();
}
double value = nextDouble() * bound;
if (value == bound) {
value = Math.nextDown(value);
}
return value;
}
public double nextDouble(double origin, double bound) {
if (origin >= bound) {
throw new IllegalArgumentException();
}
return origin + nextDouble(bound - origin);
}
@Import(name = "teavm_rand") @Import(name = "teavm_rand")
@Unmanaged @Unmanaged
private static native double crand(); private static native double crand();
@ -178,329 +83,27 @@ public class TRandom extends TObject implements TSerializable {
* Generate a random number with Gaussian distribution: * Generate a random number with Gaussian distribution:
* centered around 0 with a standard deviation of 1.0. * centered around 0 with a standard deviation of 1.0.
*/ */
@Override
public double nextGaussian() { public double nextGaussian() {
/* /*
* This implementation uses the polar method to generate two gaussian * This implementation uses the polar method to generate two gaussian
* values at a time. One is returned, and the other is stored to be returned * values at a time. One is returned, and the other is stored to be returned
* next time. * next time.
*/ */
if (haveStoredGaussian) { if (haveStoredGaussian) {
haveStoredGaussian = false; haveStoredGaussian = false;
return storedGaussian; return storedGaussian;
} }
double v1; double[] pair = RandomUtils.pairGaussian(this::nextDouble);
double v2;
double s;
do {
v1 = 2 * nextDouble() - 1;
v2 = 2 * nextDouble() - 1;
s = v1 * v1 + v2 * v2;
} while (s >= 1 || s == 0);
double m = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
storedGaussian = v2 * m;
haveStoredGaussian = true; haveStoredGaussian = true;
storedGaussian = pair[1];
return v1 * m; return pair[0];
} }
@JSBody(script = "return Math.random();") @JSBody(script = "return Math.random();")
@Import(module = "teavmMath", name = "random") @Import(module = "teavmMath", name = "random")
@Unmanaged @Unmanaged
private static native double random(); private static native double random();
public TIntStream ints(long streamSize) {
if (streamSize < 0) {
throw new IllegalArgumentException();
}
return new TSimpleIntStreamImpl() {
private long remaining = streamSize;
@Override
public boolean next(IntPredicate consumer) {
while (remaining > 0) {
--remaining;
if (!consumer.test(nextInt())) {
return true;
}
}
return false;
}
};
}
public TIntStream ints() {
return new TSimpleIntStreamImpl() {
@Override
public boolean next(IntPredicate consumer) {
while (consumer.test(nextInt())) {
// go on
}
return true;
}
};
}
public TIntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound) {
if (streamSize < 0 || randomNumberOrigin >= randomNumberBound) {
throw new IllegalArgumentException();
}
int range = randomNumberBound - randomNumberOrigin;
if (range > 0) {
return new TSimpleIntStreamImpl() {
long remaining = streamSize;
@Override
public boolean next(IntPredicate consumer) {
while (remaining > 0) {
--remaining;
if (!consumer.test(nextInt(range) + randomNumberOrigin)) {
return true;
}
}
return false;
}
};
} else {
return new TSimpleIntStreamImpl() {
long remaining = streamSize;
@Override
public boolean next(IntPredicate consumer) {
while (remaining > 0) {
--remaining;
int n;
do {
n = nextInt();
} while (n < randomNumberOrigin || n >= randomNumberBound);
if (!consumer.test(n)) {
return true;
}
}
return false;
}
};
}
}
public TIntStream ints(int randomNumberOrigin, int randomNumberBound) {
if (randomNumberOrigin >= randomNumberBound) {
throw new IllegalArgumentException();
}
int range = randomNumberBound - randomNumberOrigin;
if (range > 0) {
return new TSimpleIntStreamImpl() {
@Override
public boolean next(IntPredicate consumer) {
while (true) {
if (!consumer.test(nextInt(range) + randomNumberOrigin)) {
return true;
}
}
}
};
} else {
return new TSimpleIntStreamImpl() {
@Override
public boolean next(IntPredicate consumer) {
while (true) {
int n;
do {
n = nextInt();
} while (n < randomNumberOrigin || n >= randomNumberBound);
if (!consumer.test(n)) {
return true;
}
}
}
};
}
}
public TLongStream longs(long streamSize) {
if (streamSize < 0) {
throw new IllegalArgumentException();
}
return new TSimpleLongStreamImpl() {
private long remaining = streamSize;
@Override
public boolean next(LongPredicate consumer) {
while (remaining > 0) {
--remaining;
if (!consumer.test(nextLong())) {
return true;
}
}
return false;
}
};
}
public TLongStream longs() {
return new TSimpleLongStreamImpl() {
@Override
public boolean next(LongPredicate consumer) {
while (consumer.test(nextLong())) {
// go on
}
return true;
}
};
}
public TLongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound) {
if (streamSize < 0 || randomNumberOrigin >= randomNumberBound) {
throw new IllegalArgumentException();
}
long range = randomNumberBound - randomNumberOrigin;
if (range > 0) {
return new TSimpleLongStreamImpl() {
long remaining = streamSize;
@Override
public boolean next(LongPredicate consumer) {
while (remaining > 0) {
--remaining;
if (!consumer.test(nextLong(range) + randomNumberOrigin)) {
return true;
}
}
return false;
}
};
} else {
return new TSimpleLongStreamImpl() {
long remaining = streamSize;
@Override
public boolean next(LongPredicate consumer) {
while (remaining > 0) {
--remaining;
long n;
do {
n = nextLong();
} while (n < randomNumberOrigin || n >= randomNumberBound);
if (!consumer.test(n)) {
return true;
}
}
return false;
}
};
}
}
public TLongStream longs(long randomNumberOrigin, long randomNumberBound) {
if (randomNumberOrigin >= randomNumberBound) {
throw new IllegalArgumentException();
}
long range = randomNumberBound - randomNumberOrigin;
if (range > 0) {
return new TSimpleLongStreamImpl() {
@Override
public boolean next(LongPredicate consumer) {
while (true) {
if (!consumer.test(nextLong(range) + randomNumberOrigin)) {
return true;
}
}
}
};
} else {
return new TSimpleLongStreamImpl() {
@Override
public boolean next(LongPredicate consumer) {
while (true) {
long n;
do {
n = nextLong();
} while (n < randomNumberOrigin || n >= randomNumberBound);
if (!consumer.test(n)) {
return true;
}
}
}
};
}
}
public TDoubleStream doubles(long streamSize) {
if (streamSize < 0) {
throw new IllegalArgumentException();
}
return new TSimpleDoubleStreamImpl() {
private long remaining = streamSize;
@Override
public boolean next(DoublePredicate consumer) {
while (remaining > 0) {
--remaining;
if (!consumer.test(nextDouble())) {
return true;
}
}
return false;
}
};
}
public TDoubleStream doubles() {
return new TSimpleDoubleStreamImpl() {
@Override
public boolean next(DoublePredicate consumer) {
while (consumer.test(nextDouble())) {
// go on
}
return true;
}
};
}
public TDoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound) {
if (streamSize < 0 || randomNumberOrigin >= randomNumberBound) {
throw new IllegalArgumentException();
}
double range = randomNumberBound - randomNumberOrigin;
return new TSimpleDoubleStreamImpl() {
long remaining = streamSize;
@Override
public boolean next(DoublePredicate consumer) {
while (remaining > 0) {
--remaining;
if (!consumer.test(nextDouble() * range + randomNumberOrigin)) {
return true;
}
}
return false;
}
};
}
public TDoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
if (randomNumberOrigin >= randomNumberBound) {
throw new IllegalArgumentException();
}
double range = randomNumberBound - randomNumberOrigin;
return new TSimpleDoubleStreamImpl() {
@Override
public boolean next(DoublePredicate consumer) {
while (true) {
if (!consumer.test(nextDouble() * range + randomNumberOrigin)) {
return true;
}
}
}
};
}
} }

View File

@ -0,0 +1,223 @@
/*
* Copyright 2023 ihromant.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.util.random;
import org.teavm.classlib.impl.RandomUtils;
import org.teavm.classlib.java.util.stream.TDoubleStream;
import org.teavm.classlib.java.util.stream.TIntStream;
import org.teavm.classlib.java.util.stream.TLongStream;
public interface TRandomGenerator {
default boolean isDeprecated() {
return false;
}
default TDoubleStream doubles() {
return TDoubleStream.generate(this::nextDouble);
}
default TDoubleStream doubles(double origin, double bound) {
RandomUtils.checkRange(origin, bound);
return TDoubleStream.generate(() -> nextDouble(origin, bound));
}
default TDoubleStream doubles(long streamSize) {
RandomUtils.checkStreamSize(streamSize);
return doubles().limit(streamSize);
}
default TDoubleStream doubles(long streamSize, double origin,
double bound) {
RandomUtils.checkStreamSize(streamSize);
RandomUtils.checkRange(origin, bound);
return doubles(origin, bound).limit(streamSize);
}
default TIntStream ints() {
return TIntStream.generate(this::nextInt);
}
default TIntStream ints(int origin, int bound) {
RandomUtils.checkRange(origin, bound);
return TIntStream.generate(() -> nextInt(origin, bound));
}
default TIntStream ints(long streamSize) {
RandomUtils.checkStreamSize(streamSize);
return ints().limit(streamSize);
}
default TIntStream ints(long streamSize, int origin,
int bound) {
RandomUtils.checkStreamSize(streamSize);
RandomUtils.checkRange(origin, bound);
return ints(origin, bound).limit(streamSize);
}
default TLongStream longs() {
return TLongStream.generate(this::nextLong);
}
default TLongStream longs(long origin, long bound) {
RandomUtils.checkRange(origin, bound);
return TLongStream.generate(() -> nextLong(origin, bound));
}
default TLongStream longs(long streamSize) {
RandomUtils.checkStreamSize(streamSize);
return longs().limit(streamSize);
}
default TLongStream longs(long streamSize, long origin,
long bound) {
RandomUtils.checkStreamSize(streamSize);
RandomUtils.checkRange(origin, bound);
return longs(origin, bound).limit(streamSize);
}
default boolean nextBoolean() {
return nextInt() < 0;
}
default void nextBytes(byte[] bytes) {
if (bytes.length == 0) {
return;
}
int len = (bytes.length - 1) / Integer.BYTES + 1;
for (int i = 0; i < len; i++) {
int rnd = nextInt();
for (int j = 0; j < Integer.BYTES; j++) {
int idx = Integer.BYTES * i + j;
if (idx < bytes.length) {
bytes[idx] = (byte) (0xFF & (rnd >> (i * Byte.SIZE)));
}
}
}
}
default float nextFloat() {
return (nextInt() >>> 8) * 0x1.0p-24f;
}
default float nextFloat(float bound) {
RandomUtils.checkBound(bound);
float res = nextFloat() * bound;
if (res >= bound) {
res = Math.nextDown(bound);
}
return res;
}
default float nextFloat(float origin, float bound) {
RandomUtils.checkRange(origin, bound);
float res = nextFloat() * (bound - origin) + origin;
if (res >= bound) {
res = Math.nextAfter(bound, origin);
}
return res;
}
default double nextDouble() {
return (nextLong() >>> 11) * 0x1.0p-53;
}
default double nextDouble(double bound) {
RandomUtils.checkBound(bound);
double res = nextDouble() * bound;
if (res >= bound) {
res = Math.nextDown(bound);
}
return res;
}
default double nextDouble(double origin, double bound) {
RandomUtils.checkRange(origin, bound);
double res = nextDouble() * (bound - origin) + origin;
if (res >= bound) {
res = Math.nextAfter(bound, origin);
}
return res;
}
default int nextInt() {
return (int) (nextLong() >>> 32);
}
default int nextInt(int bound) {
RandomUtils.checkBound(bound);
int mask = (Integer.highestOneBit(bound) << 1) - 1;
while (true) {
int res = nextInt() & mask;
if (res < bound) {
return res;
}
}
}
default int nextInt(int origin, int bound) {
RandomUtils.checkRange(origin, bound);
int range = bound - origin;
if (range > 0) {
return nextInt(range) + origin;
} else {
while (true) {
int res = nextInt();
if (res >= origin && res < bound) {
return res;
}
}
}
}
long nextLong();
default long nextLong(long bound) {
RandomUtils.checkBound(bound);
long mask = (Long.highestOneBit(bound) << 1) - 1;
while (true) {
long res = nextLong() & mask;
if (res < bound) {
return res;
}
}
}
default long nextLong(long origin, long bound) {
RandomUtils.checkRange(origin, bound);
long range = bound - origin;
if (range > 0) {
return nextLong(range) + origin;
} else {
while (true) {
long res = nextLong();
if (res >= origin && res < bound) {
return res;
}
}
}
}
default double nextGaussian() {
return RandomUtils.pairGaussian(this::nextDouble)[0];
}
default double nextGaussian(double mean, double stddev) {
if (stddev < 0.0) {
throw new IllegalArgumentException();
}
return mean + stddev * nextGaussian();
}
}

View File

@ -0,0 +1,171 @@
/*
* Copyright 2023 ihromant.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
import org.teavm.junit.WholeClassCompilation;
@RunWith(TeaVMTestRunner.class)
@WholeClassCompilation
public class RandomTest {
@Test
public void testDoubles() {
Random random = new Random();
double[] doubles = IntStream.range(0, 1000).mapToDouble(i -> random.nextDouble()).toArray();
for (double d : doubles) {
assertTrue(d >= 0.0 && d < 1.0);
}
try {
random.nextDouble(-1.0);
fail();
} catch (IllegalArgumentException e) {
// normal
}
doubles = IntStream.range(0, 1000).mapToDouble(i -> random.nextDouble(20.0)).toArray();
for (double d : doubles) {
assertTrue(d >= 0.0 && d < 20.0);
}
try {
random.nextDouble(-1.0, -2.0);
fail();
} catch (IllegalArgumentException e) {
// normal
}
doubles = IntStream.range(0, 1000).mapToDouble(i -> random.nextDouble(-2.0, -1.0)).toArray();
for (double d : doubles) {
assertTrue(d >= -2.0 && d < 1.0);
}
}
@Test
public void testIntegers() {
Random random = new Random();
int[] ints = IntStream.range(0, 10000).map(i -> random.nextInt())
.toArray(); // 10 000 enough for almost 100% probability
int ones = IntStream.of(ints).reduce(0, (id, i) -> id | i);
Set<Integer> unique = Arrays.stream(ints).boxed().collect(Collectors.toSet());
assertEquals(-1, ones); // all ones present
assertTrue(unique.size() > 9900);
try {
random.nextInt(-5);
fail();
} catch (IllegalArgumentException e) {
// normal
}
ints = IntStream.range(0, 1000).map(i -> random.nextInt(512)).toArray();
for (int i : ints) {
assertTrue(i >= 0 && i < 512);
}
ints = IntStream.range(0, 1000).map(i -> random.nextInt(20)).toArray();
for (int i : ints) {
assertTrue(i >= 0 && i < 20);
}
try {
random.nextInt(-3, -5);
fail();
} catch (IllegalArgumentException e) {
// normal
}
ints = IntStream.range(0, 1000).map(i -> random.nextInt(Integer.MIN_VALUE / 3 * 2, Integer.MAX_VALUE / 3 * 2))
.toArray();
for (int i : ints) {
assertTrue(i >= Integer.MIN_VALUE / 3 * 2 && i < Integer.MAX_VALUE / 3 * 2);
}
Arrays.stream(ints).anyMatch(i -> i < Integer.MIN_VALUE / 2);
Arrays.stream(ints).anyMatch(i -> i > Integer.MAX_VALUE / 2);
}
@Test
public void testLongs() {
Random random = new Random();
long[] longs = IntStream.range(0, 10000).mapToLong(i -> random.nextLong())
.toArray(); // 10 000 enough for almost 100% probability
long ones = LongStream.of(longs).reduce(0L, (id, i) -> id | i);
Set<Long> unique = Arrays.stream(longs).boxed().collect(Collectors.toSet());
assertEquals(-1L, ones); // all ones present
assertTrue(unique.size() > 9900);
try {
random.nextLong(-5);
fail();
} catch (IllegalArgumentException e) {
// normal
}
longs = IntStream.range(0, 1000).mapToLong(i -> random.nextLong(512L)).toArray();
for (long l : longs) {
assertTrue(l >= 0L && l < 512L);
}
longs = IntStream.range(0, 1000).mapToLong(i -> random.nextLong(20L)).toArray();
for (long l : longs) {
assertTrue(l >= 0 && l < 20);
}
try {
random.nextLong(-3, -5);
fail();
} catch (IllegalArgumentException e) {
// normal
}
longs = IntStream.range(0, 1000).mapToLong(i -> random.nextLong(Long.MIN_VALUE / 3 * 2, Long.MAX_VALUE / 3 * 2))
.toArray();
for (long l : longs) {
assertTrue(l >= Long.MIN_VALUE / 3 * 2 && l < Long.MAX_VALUE / 3 * 2);
}
Arrays.stream(longs).anyMatch(l -> l < Long.MIN_VALUE / 2);
Arrays.stream(longs).anyMatch(l -> l > Long.MAX_VALUE / 2);
}
@Test
public void testNextBytes() {
Random rand = new Random();
rand.nextBytes(new byte[0]);
assertTrue(IntStream.range(0, 1000).anyMatch(i -> testNonZero(rand, 5)));
assertTrue(IntStream.range(0, 1000).anyMatch(i -> testNonZero(rand, 6)));
assertTrue(IntStream.range(0, 1000).anyMatch(i -> testNonZero(rand, 7)));
assertTrue(IntStream.range(0, 1000).anyMatch(i -> testNonZero(rand, 8)));
byte[] bytes = new byte[1000];
rand.nextBytes(bytes);
assertEquals(0xFF, IntStream.range(0, bytes.length).map(i -> 0xFF & bytes[i]).reduce(0, (id, i) -> id | i));
}
private boolean testNonZero(Random rand, int size) {
byte[] bytes = new byte[size];
rand.nextBytes(bytes);
for (byte b : bytes) {
if (b == 0) {
return false;
}
}
return true;
}
@Test
public void testGaussian() {
Random rand = new Random();
double[] doubles = IntStream.range(0, 10000).mapToDouble(i -> rand.nextGaussian(30, 10)).toArray();
assertTrue(DoubleStream.of(doubles).filter(d -> d < 0.0 || d > 60.0).count() < 100);
}
}