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;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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