mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
classlib: update RandomGenerator implementation (#743)
This commit is contained in:
parent
ef818ac4c5
commit
4a081db1c3
|
@ -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 };
|
||||
}
|
||||
}
|
|
@ -15,32 +15,23 @@
|
|||
*/
|
||||
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.classlib.PlatformDetector;
|
||||
import org.teavm.classlib.impl.RandomUtils;
|
||||
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.util.stream.TDoubleStream;
|
||||
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.classlib.java.util.random.TRandomGenerator;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
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() */
|
||||
private double storedGaussian;
|
||||
|
||||
|
||||
/** Whether storedGuassian value is valid */
|
||||
private boolean haveStoredGaussian;
|
||||
|
||||
|
||||
public TRandom() {
|
||||
}
|
||||
|
||||
|
@ -50,24 +41,12 @@ public class TRandom extends TObject implements TSerializable {
|
|||
public void setSeed(@SuppressWarnings("unused") long seed) {
|
||||
}
|
||||
|
||||
protected int next(int bits) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
return next(32);
|
||||
return (int) (0x1.0p+32 * nextDouble() + Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt(int n) {
|
||||
if (n <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
|
@ -75,73 +54,17 @@ public class TRandom extends TObject implements TSerializable {
|
|||
return (int) (nextDouble() * n);
|
||||
}
|
||||
|
||||
public int nextInt(int origin, int bound) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextLong() {
|
||||
return ((long) nextInt() << 32) | nextInt();
|
||||
}
|
||||
|
||||
public long nextLong(long bound) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float nextFloat() {
|
||||
return (float) nextDouble();
|
||||
}
|
||||
|
||||
public float nextFloat(float bound) {
|
||||
return (float) nextDouble(bound);
|
||||
}
|
||||
|
||||
public float nextFloat(float origin, float bound) {
|
||||
return (float) nextDouble(origin, bound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextDouble() {
|
||||
if (PlatformDetector.isC()) {
|
||||
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")
|
||||
@Unmanaged
|
||||
private static native double crand();
|
||||
|
@ -178,329 +83,27 @@ public class TRandom extends TObject implements TSerializable {
|
|||
* Generate a random number with Gaussian distribution:
|
||||
* centered around 0 with a standard deviation of 1.0.
|
||||
*/
|
||||
@Override
|
||||
public double nextGaussian() {
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (haveStoredGaussian) {
|
||||
haveStoredGaussian = false;
|
||||
return storedGaussian;
|
||||
}
|
||||
|
||||
double v1;
|
||||
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;
|
||||
double[] pair = RandomUtils.pairGaussian(this::nextDouble);
|
||||
haveStoredGaussian = true;
|
||||
storedGaussian = pair[1];
|
||||
|
||||
return v1 * m;
|
||||
return pair[0];
|
||||
}
|
||||
|
||||
@JSBody(script = "return Math.random();")
|
||||
@Import(module = "teavmMath", name = "random")
|
||||
@Unmanaged
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
171
tests/src/test/java/org/teavm/classlib/java/util/RandomTest.java
Normal file
171
tests/src/test/java/org/teavm/classlib/java/util/RandomTest.java
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user