mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8346107: Generators: testing utility for random value generation
Co-authored-by: Emanuel Peter <epeter@openjdk.org> Reviewed-by: epeter, chagedorn
This commit is contained in:
parent
4ad5d5514e
commit
f64f22b360
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* Provides an any-bits double distribution random generator, i.e. the bits are uniformly sampled,
|
||||
* thus creating any possible double value, including the multiple different NaN representations.
|
||||
*/
|
||||
final class AnyBitsDoubleGenerator extends BoundGenerator<Double> {
|
||||
/**
|
||||
* Create a new {@link AnyBitsDoubleGenerator}.
|
||||
*/
|
||||
public AnyBitsDoubleGenerator(Generators g) {
|
||||
super(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double next() {
|
||||
return Double.longBitsToDouble(g.random.nextLong());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* Provides an any-bits float distribution random generator, i.e. the bits are uniformly sampled,
|
||||
* thus creating any possible float value, including the multiple different NaN representations.
|
||||
*/
|
||||
final class AnyBitsFloatGenerator extends BoundGenerator<Float> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link AnyBitsFloatGenerator}.
|
||||
*/
|
||||
public AnyBitsFloatGenerator(Generators g) {
|
||||
super(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float next() {
|
||||
return Float.intBitsToFloat(g.random.nextInt());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* This is a common superclass for all generators that maintain a reference to the Generators object that created them.
|
||||
* This allows internally creating other generators or using the {@link RandomnessSource} provided in
|
||||
* {@link Generators#random}.
|
||||
*/
|
||||
abstract class BoundGenerator<T> implements Generator<T> {
|
||||
Generators g;
|
||||
|
||||
BoundGenerator(Generators g) {
|
||||
this.g = g;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* An EmptyGeneratorException is thrown if a generator configuration is requested that would result in an empty
|
||||
* set of values. For example, bounds such as [1, 0] cause an EmptyGeneratorException. Another example would be
|
||||
* restricting a uniform integer generator over the range [0, 1] to [10, 11].
|
||||
*/
|
||||
public class EmptyGeneratorException extends RuntimeException {
|
||||
public EmptyGeneratorException() {}
|
||||
}
|
||||
34
test/hotspot/jtreg/compiler/lib/generators/Generator.java
Normal file
34
test/hotspot/jtreg/compiler/lib/generators/Generator.java
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* A stream of values according to a specific distribution.
|
||||
*/
|
||||
public interface Generator<T> {
|
||||
/**
|
||||
* Returns the next value from the stream.
|
||||
*/
|
||||
T next();
|
||||
}
|
||||
584
test/hotspot/jtreg/compiler/lib/generators/Generators.java
Normal file
584
test/hotspot/jtreg/compiler/lib/generators/Generators.java
Normal file
@ -0,0 +1,584 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.util.*;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
/**
|
||||
* The Generators class provides a set of random generator functions for testing.
|
||||
* The goal is to cover many special cases, such as NaNs in Floats or values
|
||||
* close to overflow in ints. They should produce values from specific
|
||||
* "interesting" distributions which might trigger various behaviours in
|
||||
* optimizations.
|
||||
* <p>
|
||||
* Normally, clients get the default Generators instance by referring to the static variable {@link #G}.
|
||||
* <p>
|
||||
* The Generators class offers generators with essential distributions, for example, {@link #uniformInts(int, int)},
|
||||
* {@link #uniformLongs(long, long)}, {@link #uniformDoubles(double, double)} or {@link #uniformFloats()}. For floating
|
||||
* points, you may choose to get random bit patterns uniformly at random, rather than the values they represent.
|
||||
* The Generators class also offers special generators of interesting values such as {@link #powerOfTwoInts(int)},
|
||||
* {@link #powerOfTwoLongs(int)}, which are values close to the powers of 2, or {@link #SPECIAL_DOUBLES} and
|
||||
* {@link #SPECIAL_FLOATS}, which are values such as infinity, NaN, zero or the maximum and minimum values.
|
||||
* <p>
|
||||
* Many distributions are <i>restrictable</i>. For example, if you first create a uniform integer generator over [1, 10],
|
||||
* you can obtain a new generator by further restricting this range to [1, 5]. This is useful in cases where a function
|
||||
* should be tested with different distributions. For example, a function <code>h(int, int, int)</code> under test might
|
||||
* be worthwhile to test not only with uniformly sampled integers but might also exhibit interesting behavior if tested
|
||||
* specifically with powers of two. Suppose further that each argument has a different range of allowed values. We
|
||||
* can write a test function as below:
|
||||
*
|
||||
* <pre><code>
|
||||
* void test(Generator{@literal <Integer>} g) {
|
||||
* h(g.restricted(1, 10).next(), g.next(), g.restricted(-10, 100).next());
|
||||
* }
|
||||
* </code></pre>
|
||||
*
|
||||
* Then <code>test</code> can be called with different distributions, for example:
|
||||
*
|
||||
* <pre><code>
|
||||
* test(G.uniformInts());
|
||||
* test(G.specialInts(0));
|
||||
* </code></pre>
|
||||
* <p>
|
||||
* If there is a single value that is interesting as an argument to all three parameters, we might even call this
|
||||
* method with a single generator, ensuring that the single value is within the restriction ranges:
|
||||
*
|
||||
* <pre><code>
|
||||
* test(G.single(1));
|
||||
* </code></pre>
|
||||
*
|
||||
* <p>
|
||||
* Furthermore, this class offers utility generators, such as {@link #randomElement(Collection)} or
|
||||
* {@link #orderedRandomElement(Collection)} for sampling from a list of elements; {@link #single(Object)} for a
|
||||
* generator that only produces a single value; and {@link #mixed(Generator, Generator, int, int)} which combines
|
||||
* two generators with the provided weights.
|
||||
* <p>
|
||||
* Thus, the generators provided by this class are composable and therefore extensible. This allows to easily
|
||||
* create random generators even with types and distributions that are not predefined. For example, to create a
|
||||
* generator that provides true with 60 percent probably and false with 40 percent probably, one can simply write:
|
||||
* <pre><code>G.mixed(G.single(true), G.single(false), 60, 40)</code></pre>
|
||||
* <p>
|
||||
* Generators are by no means limited to work with numbers. Restrictable generators can work with any type that
|
||||
* implements {@link Comparable} while generators such as {@link #randomElement(Collection)} and {@link #single(Object)}
|
||||
* work with any type. Note that there are separate restrictable versions of the last two generators
|
||||
* (namely, {@link #orderedRandomElement(Collection)} and {@link #single(Comparable)}) that work with comparable types.
|
||||
* For example, you might restrict a generator choosing strings at random:
|
||||
* <pre><code>G.orderedRandomElement(List.of("Bob", "Alice", "Carol")).restricted("Al", "Bz")</code></pre>
|
||||
* This returns a new generator which only returns elements greater or equal than "Al" and less than or equal to
|
||||
* "Bz". Thus, the only two values remaining in the example are "Alice" and "Bob". In general, you should always refer
|
||||
* to the method that created the generator to learn about the exact semantics of restricting it.
|
||||
* <p>
|
||||
* For all the generators created by instances of this class, the following rule applies: Integral generators are
|
||||
* always inclusive of both the lower and upper bound, while floating point generators are always inclusive of the
|
||||
* lower bound but always exclusive of the upper bound. This also applies to all generators obtained by restricting
|
||||
* these generators further.
|
||||
* <p>
|
||||
* Unless you have reasons to pick a specific distribution, you are encouraged to rely on {@link #ints()},
|
||||
* {@link #longs()}, {@link #doubles()} and {@link #floats()}, which will randomly pick an interesting distribution.
|
||||
* This is best practice, because that allows the test to be run under different conditions – maybe only a single
|
||||
* distribution can trigger a bug.
|
||||
*/
|
||||
public final class Generators {
|
||||
/**
|
||||
* This is the default Generators instance that should be used by tests normally.
|
||||
*/
|
||||
public static final Generators G = new Generators(new RandomnessSourceAdapter(Utils.getRandomInstance()));
|
||||
|
||||
final RandomnessSource random;
|
||||
|
||||
public Generators(RandomnessSource random) {
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generator that generates integers in the range [lo, hi] (inclusive of both lo and hi).
|
||||
*/
|
||||
public RestrictableGenerator<Integer> uniformInts(int lo, int hi) {
|
||||
return new UniformIntGenerator(this, lo, hi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generator that generates integers over the entire range of int.
|
||||
*/
|
||||
public RestrictableGenerator<Integer> uniformInts() {
|
||||
return uniformInts(Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generator that generates longs in the range [lo, hi] (inclusive of both lo and hi).
|
||||
*/
|
||||
public RestrictableGenerator<Long> uniformLongs(long lo, long hi) {
|
||||
return new UniformLongGenerator(this, lo, hi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generator that generates integers over the entire range of int.
|
||||
*/
|
||||
public RestrictableGenerator<Long> uniformLongs() {
|
||||
return uniformLongs(Long.MIN_VALUE, Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates uniform doubles in the range of [lo, hi) (inclusive of lo, exclusive of hi).
|
||||
*/
|
||||
public RestrictableGenerator<Double> uniformDoubles(double lo, double hi) {
|
||||
return new UniformDoubleGenerator(this, lo, hi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates uniform doubles in the range of [0, 1) (inclusive of 0, exclusive of 1).
|
||||
*/
|
||||
public RestrictableGenerator<Double> uniformDoubles() {
|
||||
return uniformDoubles(0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an any-bits double distribution random generator, i.e. the bits are uniformly sampled,
|
||||
* thus creating any possible double value, including the multiple different NaN representations.
|
||||
*/
|
||||
public Generator<Double> anyBitsDouble() {
|
||||
return new AnyBitsDoubleGenerator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates uniform doubles in the range of [lo, hi) (inclusive of lo, exclusive of hi).
|
||||
*/
|
||||
public RestrictableGenerator<Float> uniformFloats(float lo, float hi) {
|
||||
return new UniformFloatGenerator(this, lo, hi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates uniform floats in the range of [0, 1) (inclusive of 0, exclusive of 1).
|
||||
*/
|
||||
public RestrictableGenerator<Float> uniformFloats() {
|
||||
return uniformFloats(0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an any-bits float distribution random generator, i.e. the bits are uniformly sampled,
|
||||
* thus creating any possible float value, including the multiple different NaN representations.
|
||||
*/
|
||||
public Generator<Float> anyBitsFloats() {
|
||||
return new AnyBitsFloatGenerator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generator that uniformly randomly samples elements from the provided collection.
|
||||
* Each element in the collection is treated as a separate, unique value, even if equals might be true.
|
||||
* The result is an unrestrictable generator. If you want a restrictable generator that selects values from a
|
||||
* list and are working with Comparable values, use {@link #orderedRandomElement(Collection)}.
|
||||
*/
|
||||
public <T> Generator<T> randomElement(Collection<T> list) {
|
||||
return new RandomElementGenerator<>(this, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a restrictable generator that uniformly randomly samples elements from the provided collection.
|
||||
* Duplicate elements are discarded from the collection. Restrictions are inclusive of both the uppper and lower
|
||||
* bound.
|
||||
*/
|
||||
public <T extends Comparable<T>> RestrictableGenerator<T> orderedRandomElement(Collection<T> list) {
|
||||
NavigableSet<T> set = list instanceof NavigableSet<T> ? (NavigableSet<T>) list : new TreeSet<>(list);
|
||||
return new RestrictableRandomElementGenerator<>(this, set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generator that always generate the provided value.
|
||||
*/
|
||||
public <T> Generator<T> single(T value) {
|
||||
return new SingleValueGenerator<>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a restrictable generator that always generate the provided value.
|
||||
*/
|
||||
public <T extends Comparable<T>> RestrictableGenerator<T> single(T value) {
|
||||
return new RestrictableSingleValueGenerator<>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new generator that samples its next element from either generator A or B, with assignable weights.
|
||||
* An overload for restrictable generators exists.
|
||||
*/
|
||||
public <T> Generator<T> mixed(Generator<T> a, Generator<T> b, int weightA, int weightB) {
|
||||
return new MixedGenerator<>(this, List.of(a, b), List.of(weightA, weightB));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new generator that samples its next element randomly from one of the provided generators with
|
||||
* assignable weights.
|
||||
* An overload for restrictable generators exists.
|
||||
*/
|
||||
@SafeVarargs
|
||||
public final <T> Generator<T> mixed(List<Integer> weights, Generator<T>... generators) {
|
||||
return new MixedGenerator<>(this, Arrays.asList(generators), weights);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new restrictable generator that samples its next element from either generator A or B, with assignable weights.
|
||||
* Restricting this generator restricts each subgenerator. Generators which become empty by the restriction are
|
||||
* removed from the new mixed generator. Weights stay their original value if a generator is removed. If the mixed
|
||||
* generator would become empty by applying a restriction {@link EmptyGeneratorException} is thrown.
|
||||
*/
|
||||
public <T extends Comparable<T>> RestrictableGenerator<T> mixed(RestrictableGenerator<T> a, RestrictableGenerator<T> b, int weightA, int weightB) {
|
||||
return new RestrictableMixedGenerator<>(this, List.of(a, b), List.of(weightA, weightB));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new restrictable generator that samples its next element randomly from one of the provided restrictable
|
||||
* generators with assignable weights.
|
||||
* See {@link #mixed(RestrictableGenerator, RestrictableGenerator, int, int)} for details about restricting this
|
||||
* generator.
|
||||
*/
|
||||
@SafeVarargs
|
||||
public final <T extends Comparable<T>> RestrictableGenerator<T> mixed(List<Integer> weights, RestrictableGenerator<T>... generators) {
|
||||
return new RestrictableMixedGenerator<>(this, Arrays.asList(generators), weights);
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly pick an int generator.
|
||||
*
|
||||
* @return Random int generator.
|
||||
*/
|
||||
public RestrictableGenerator<Integer> ints() {
|
||||
switch(random.nextInt(0, 6)) {
|
||||
case 0 -> { return uniformInts(); }
|
||||
case 1 -> { return powerOfTwoInts(0); }
|
||||
case 2 -> { return powerOfTwoInts(2); }
|
||||
case 3 -> { return powerOfTwoInts(16); }
|
||||
case 4 -> { return uniformIntsMixedWithPowersOfTwo(1, 1, 16); }
|
||||
case 5 -> { return uniformIntsMixedWithPowersOfTwo(1, 2, 2); }
|
||||
default -> { throw new RuntimeException("impossible"); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A generator of special ints. Special ints are powers of two or values close to powers of 2, where a value
|
||||
* is close to a power of two p if it is in the interval [p - range, p + range]. Note that we also consider negative
|
||||
* values as powers of two. Note that for range >= 1, the set of values includes {@link Integer#MAX_VALUE} and
|
||||
* {@link Integer#MIN_VALUE}.
|
||||
*/
|
||||
public RestrictableGenerator<Integer> powerOfTwoInts(int range) {
|
||||
TreeSet<Integer> set = new TreeSet<>();
|
||||
for (int i = 0; i < 32; i++) {
|
||||
int pow2 = 1 << i;
|
||||
for (int j = -range; j <= range; j++) {
|
||||
set.add(+pow2 + j);
|
||||
set.add(-pow2 + j);
|
||||
}
|
||||
}
|
||||
return orderedRandomElement(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience helper to mix {@link #powerOfTwoInts(int)} with {@link #uniformInts(int, int)}.
|
||||
*/
|
||||
public RestrictableGenerator<Integer> uniformIntsMixedWithPowersOfTwo(int weightUniform, int weightSpecial, int rangeSpecial) {
|
||||
return mixed(uniformInts(), powerOfTwoInts(rangeSpecial), weightUniform, weightSpecial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly pick a long generator.
|
||||
*
|
||||
* @return Random long generator.
|
||||
*/
|
||||
public RestrictableGenerator<Long> longs() {
|
||||
switch(random.nextInt(0, 6)) {
|
||||
case 0 -> { return uniformLongs(); }
|
||||
case 1 -> { return powerOfTwoLongs(0); }
|
||||
case 2 -> { return powerOfTwoLongs(2); }
|
||||
case 3 -> { return powerOfTwoLongs(16); }
|
||||
case 4 -> { return uniformLongsMixedWithPowerOfTwos(1, 1, 16); }
|
||||
case 5 -> { return uniformLongsMixedWithPowerOfTwos(1, 2, 2); }
|
||||
default -> { throw new RuntimeException("impossible"); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A generator of special longs. Special longs are powers of two or values close to powers of 2, where a value
|
||||
* is close to a power of two p if it is in the interval [p - range, p + range]. Note that we also consider negative
|
||||
* values as powers of two. Note that for range >= 1, the set of values includes {@link Long#MAX_VALUE} and
|
||||
* {@link Long#MIN_VALUE}.
|
||||
*/
|
||||
public RestrictableGenerator<Long> powerOfTwoLongs(int range) {
|
||||
TreeSet<Long> set = new TreeSet<>();
|
||||
for (int i = 0; i < 64; i++) {
|
||||
long pow2 = 1L << i;
|
||||
for (int j = -range; j <= range; j++) {
|
||||
set.add(+pow2 + j);
|
||||
set.add(-pow2 + j);
|
||||
}
|
||||
}
|
||||
return orderedRandomElement(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience helper to mix {@link #powerOfTwoLongs(int)} with {@link #uniformLongs(long, long)}.
|
||||
*/
|
||||
public RestrictableGenerator<Long> uniformLongsMixedWithPowerOfTwos(int weightUniform, int weightSpecial, int rangeSpecial) {
|
||||
return mixed(uniformLongs(), powerOfTwoLongs(rangeSpecial), weightUniform, weightSpecial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly pick a float generator.
|
||||
*
|
||||
* @return Random float generator.
|
||||
*/
|
||||
public Generator<Float> floats() {
|
||||
switch(random.nextInt(0, 5)) {
|
||||
case 0 -> { return uniformFloats(-1, 1); }
|
||||
// Well-balanced, so that multiplication reduction never explodes or collapses to zero:
|
||||
case 1 -> { return uniformFloats(0.999f, 1.001f); }
|
||||
case 2 -> { return anyBitsFloats(); }
|
||||
// A tame distribution, mixed in with the occasional special float value:
|
||||
case 3 -> { return mixedWithSpecialFloats(uniformFloats(0.999f, 1.001f), 10, 1000); }
|
||||
// Generating any bits, but special values are more frequent.
|
||||
case 4 -> { return mixedWithSpecialFloats(anyBitsFloats(), 100, 200); }
|
||||
default -> { throw new RuntimeException("impossible"); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly pick a double generator.
|
||||
*
|
||||
* @return Random double generator.
|
||||
*/
|
||||
public Generator<Double> doubles() {
|
||||
switch(random.nextInt(0, 5)) {
|
||||
case 0 -> { return uniformDoubles(-1, 1); }
|
||||
// Well-balanced, so that multiplication reduction never explodes or collapses to zero:
|
||||
case 1 -> { return uniformDoubles(0.999f, 1.001f); }
|
||||
case 2 -> { return anyBitsDouble(); }
|
||||
// A tame distribution, mixed in with the occasional special double value:
|
||||
case 3 -> { return mixedWithSpecialDoubles(uniformDoubles(0.999f, 1.001f), 10, 1000); }
|
||||
// Generating any bits, but special values are more frequent.
|
||||
case 4 -> { return mixedWithSpecialDoubles(anyBitsDouble(), 100, 200); }
|
||||
default -> { throw new RuntimeException("impossible"); }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates interesting double values, which often are corner cases such as, 0, 1, -1, NaN, +/- Infinity, Min,
|
||||
* Max.
|
||||
*/
|
||||
public final RestrictableGenerator<Double> SPECIAL_DOUBLES = orderedRandomElement(List.of(
|
||||
0d,
|
||||
1d,
|
||||
-1d,
|
||||
Double.POSITIVE_INFINITY,
|
||||
Double.NEGATIVE_INFINITY,
|
||||
Double.NaN,
|
||||
Double.MAX_VALUE,
|
||||
Double.MIN_NORMAL,
|
||||
Double.MIN_VALUE
|
||||
));
|
||||
|
||||
/**
|
||||
* Returns a mixed generator that mixes the provided background generator and {@link #SPECIAL_DOUBLES} with the provided
|
||||
* weights.
|
||||
*/
|
||||
public Generator<Double> mixedWithSpecialDoubles(Generator<Double> background, int weightNormal, int weightSpecial) {
|
||||
return mixed(background, SPECIAL_DOUBLES, weightNormal, weightSpecial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a restrictable mixed generator that mixes the provided background generator and {@link #SPECIAL_DOUBLES} with the provided
|
||||
* weights.
|
||||
*/
|
||||
public RestrictableGenerator<Double> mixedWithSpecialDoubles(RestrictableGenerator<Double> background, int weightNormal, int weightSpecial) {
|
||||
return mixed(background, SPECIAL_DOUBLES, weightNormal, weightSpecial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates interesting double values, which often are corner cases such as, 0, 1, -1, NaN, +/- Infinity, Min,
|
||||
* Max.
|
||||
*/
|
||||
public final RestrictableGenerator<Float> SPECIAL_FLOATS = orderedRandomElement(List.of(
|
||||
0f,
|
||||
1f,
|
||||
-1f,
|
||||
Float.POSITIVE_INFINITY,
|
||||
Float.NEGATIVE_INFINITY,
|
||||
Float.NaN,
|
||||
Float.MAX_VALUE,
|
||||
Float.MIN_NORMAL,
|
||||
Float.MIN_VALUE
|
||||
));
|
||||
|
||||
/**
|
||||
* Returns a mixed generator that mixes the provided background generator and {@link #SPECIAL_FLOATS} with the provided
|
||||
* weights.
|
||||
*/
|
||||
public Generator<Float> mixedWithSpecialFloats(Generator<Float> background, int weightNormal, int weightSpecial) {
|
||||
return mixed(background, SPECIAL_FLOATS, weightNormal, weightSpecial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a restrictable mixed generator that mixes the provided background generator and {@link #SPECIAL_FLOATS} with the provided
|
||||
* weights.
|
||||
*/
|
||||
public RestrictableGenerator<Float> mixedWithSpecialFloats(RestrictableGenerator<Float> background, int weightNormal, int weightSpecial) {
|
||||
return mixed(background, SPECIAL_FLOATS, weightNormal, weightSpecial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trys to restrict the provided restrictable generator to the provided range. If the restriction fails no
|
||||
* exception is raised, but instead a uniform int generator for the range is returned.
|
||||
*/
|
||||
public RestrictableGenerator<Integer> safeRestrict(RestrictableGenerator<Integer> g, int lo, int hi) {
|
||||
try {
|
||||
return g.restricted(lo, hi);
|
||||
} catch (EmptyGeneratorException e) {
|
||||
return uniformInts(lo, hi);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trys to restrict the provided restrictable generator to the provided range. If the restriction fails no
|
||||
* exception is raised, but instead a uniform long generator for the range is returned.
|
||||
*/
|
||||
public RestrictableGenerator<Long> safeRestrict(RestrictableGenerator<Long> g, long lo, long hi) {
|
||||
try {
|
||||
return g.restricted(lo, hi);
|
||||
} catch (EmptyGeneratorException e) {
|
||||
return uniformLongs(lo, hi);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trys to restrict the provided restrictable generator to the provided range. If the restriction fails no
|
||||
* exception is raised, but instead a uniform double generator for the range is returned.
|
||||
*/
|
||||
public RestrictableGenerator<Double> safeRestrict(RestrictableGenerator<Double> g, double lo, double hi) {
|
||||
try {
|
||||
return g.restricted(lo, hi);
|
||||
} catch (EmptyGeneratorException e) {
|
||||
return uniformDoubles(lo, hi);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trys to restrict the provided restrictable generator to the provided range. If the restriction fails no
|
||||
* exception is raised, but instead a uniform float generator for the range is returned.
|
||||
*/
|
||||
public RestrictableGenerator<Float> safeRestrict(RestrictableGenerator<Float> g, float lo, float hi) {
|
||||
try {
|
||||
return g.restricted(lo, hi);
|
||||
} catch (EmptyGeneratorException e) {
|
||||
return uniformFloats(lo, hi);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the memory segments with doubles obtained by calling next on the generator.
|
||||
*
|
||||
* @param generator The generator from which to source the values.
|
||||
* @param ms Memory segment to be filled with random values.
|
||||
*/
|
||||
public void fillDouble(Generator<Double> generator, MemorySegment ms) {
|
||||
var layout = ValueLayout.JAVA_DOUBLE_UNALIGNED;
|
||||
for (long i = 0; i < ms.byteSize() / layout.byteSize(); i++) {
|
||||
ms.setAtIndex(layout, i, generator.next());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the array with doubles using the distribution of nextDouble.
|
||||
*
|
||||
* @param a Array to be filled with random values.
|
||||
*/
|
||||
public void fill(Generator<Double> generator, double[] a) {
|
||||
fillDouble(generator, MemorySegment.ofArray(a));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the memory segments with floats obtained by calling next on the generator.
|
||||
*
|
||||
* @param generator The generator from which to source the values.
|
||||
* @param ms Memory segment to be filled with random values.
|
||||
*/
|
||||
public void fillFloat(Generator<Float> generator, MemorySegment ms) {
|
||||
var layout = ValueLayout.JAVA_FLOAT_UNALIGNED;
|
||||
for (long i = 0; i < ms.byteSize() / layout.byteSize(); i++) {
|
||||
ms.setAtIndex(layout, i, generator.next());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the array with floats using the distribution of nextDouble.
|
||||
*
|
||||
* @param a Array to be filled with random values.
|
||||
*/
|
||||
public void fill(Generator<Float> generator, float[] a) {
|
||||
fillFloat(generator, MemorySegment.ofArray(a));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the memory segments with ints obtained by calling next on the generator.
|
||||
*
|
||||
* @param generator The generator from which to source the values.
|
||||
* @param ms Memory segment to be filled with random values.
|
||||
*/
|
||||
public void fillInt(Generator<Integer> generator, MemorySegment ms) {
|
||||
var layout = ValueLayout.JAVA_INT_UNALIGNED;
|
||||
for (long i = 0; i < ms.byteSize() / layout.byteSize(); i++) {
|
||||
ms.setAtIndex(layout, i, generator.next());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the array with ints using the distribution of nextDouble.
|
||||
*
|
||||
* @param a Array to be filled with random values.
|
||||
*/
|
||||
public void fill(Generator<Integer> generator, int[] a) {
|
||||
fillInt(generator, MemorySegment.ofArray(a));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the memory segments with longs obtained by calling next on the generator.
|
||||
*
|
||||
* @param generator The generator from which to source the values.
|
||||
* @param ms Memory segment to be filled with random values.
|
||||
*/
|
||||
public void fillLong(Generator<Long> generator, MemorySegment ms) {
|
||||
var layout = ValueLayout.JAVA_LONG_UNALIGNED;
|
||||
for (long i = 0; i < ms.byteSize() / layout.byteSize(); i++) {
|
||||
ms.setAtIndex(layout, i, generator.next());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the array with longs using the distribution of nextDouble.
|
||||
*
|
||||
* @param a Array to be filled with random values.
|
||||
*/
|
||||
public void fill(Generator<Long> generator, long[] a) {
|
||||
fillLong(generator, MemorySegment.ofArray(a));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Mixed results between different generators with configurable weights.
|
||||
*/
|
||||
class MixedGenerator<G extends Generator<T>, T> extends BoundGenerator<T> {
|
||||
private final TreeMap<Integer, G> generators = new TreeMap<>();
|
||||
private final int totalWeight;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MixedGenerator}, which samples from a list of generators at random,
|
||||
* according to specified weights.
|
||||
*/
|
||||
MixedGenerator(Generators g, List<G> generators, List<Integer> weights) {
|
||||
super(g);
|
||||
if (weights.size() != generators.size()) {
|
||||
throw new IllegalArgumentException("weights and generators must have the same size");
|
||||
}
|
||||
int acc = 0;
|
||||
for (int i = 0; i < generators.size(); i++) {
|
||||
int weight = weights.get(i);
|
||||
if (weight <= 0) {
|
||||
throw new IllegalArgumentException("weights must be positive");
|
||||
}
|
||||
acc += weight;
|
||||
this.generators.put(acc, generators.get(i));
|
||||
}
|
||||
this.totalWeight = acc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new mixed generator by mapping each generator of the old generator to a new value or removing it.
|
||||
* @param other The generator to copy from.
|
||||
* @param generatorMapper A function that is called for each subgenerator in the old generator. Either return a
|
||||
* generator that takes the role of the old generator (might be the same) or null to remove
|
||||
* the generator completely. In this case, the weights of the other generators stay the same.
|
||||
*/
|
||||
MixedGenerator(MixedGenerator<G, T> other, Function<G, G> generatorMapper) {
|
||||
super(other.g);
|
||||
// We could map and create new lists and delegate to the other constructor but that would allocate
|
||||
// two additional lists, so in the interest of memory efficiency we construct the new TreeMap ourselves.
|
||||
int acc = 0;
|
||||
int prevKey = 0;
|
||||
// entrySet: "The set's iterator returns the entries in ascending key order." (documentation)
|
||||
// This means we iterate over the generators exactly in the order they were inserted as we insert with ascending
|
||||
// keys (due to summing positive numbers).
|
||||
for (var entry : other.generators.entrySet()) {
|
||||
var gen = generatorMapper.apply(entry.getValue());
|
||||
if (gen != null) {
|
||||
// entry.getKey() is the sum of all generator weights up to this one.
|
||||
// We compute this generator's weight by taking the difference to the previous key
|
||||
int weight = entry.getKey() - prevKey;
|
||||
acc += weight;
|
||||
this.generators.put(acc, gen);
|
||||
}
|
||||
prevKey = entry.getKey();
|
||||
}
|
||||
if (this.generators.isEmpty()) {
|
||||
throw new EmptyGeneratorException();
|
||||
}
|
||||
this.totalWeight = acc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
int r = g.random.nextInt(0, totalWeight);
|
||||
return generators.higherEntry(r).getValue().next();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
class RandomElementGenerator<T> extends BoundGenerator<T> {
|
||||
private final ArrayList<T> elements;
|
||||
private final Generator<Integer> generator;
|
||||
|
||||
RandomElementGenerator(Generators g, Collection<T> elements) {
|
||||
super(g);
|
||||
this.elements = new ArrayList<>(elements);
|
||||
if (this.elements.isEmpty()) throw new EmptyGeneratorException();
|
||||
this.generator = g.uniformInts(0, elements.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T next() {
|
||||
return elements.get(generator.next());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* Defines the underlying randomness source used by the generators. This is essentially a subset of
|
||||
* {@link java.util.random.RandomGenerator} and the present methods have the same contract.
|
||||
* This interface greatly benefits testing, as it is much easier to implement than
|
||||
* {@link java.util.random.RandomGenerator} and thus makes creating test doubles more convenient.
|
||||
*/
|
||||
public interface RandomnessSource {
|
||||
/** Samples the next long value uniformly at random. */
|
||||
long nextLong();
|
||||
/** Samples the next long value in the half-open interval [lo, hi) uniformly at random. */
|
||||
long nextLong(long lo, long hi);
|
||||
/** Samples the next int value uniformly at random. */
|
||||
int nextInt();
|
||||
/** Samples the next int value in the half-open interval [lo, hi) uniformly at random. */
|
||||
int nextInt(int lo, int hi);
|
||||
/** Samples the next double value in the half-open interval [lo, hi) uniformly at random. */
|
||||
double nextDouble(double lo, double hi);
|
||||
/** Samples the next float value in the half-open interval [lo, hi) uniformly at random. */
|
||||
float nextFloat(float lo, float hi);
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
import java.util.random.RandomGenerator;
|
||||
|
||||
/**
|
||||
* An adapter for using a {@link RandomGenerator} as a {@link RandomnessSource}.
|
||||
* See RandomnessSource for more information.
|
||||
*/
|
||||
public class RandomnessSourceAdapter implements RandomnessSource {
|
||||
private final RandomGenerator rand;
|
||||
|
||||
RandomnessSourceAdapter(RandomGenerator rand) {
|
||||
this.rand = rand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextLong() {
|
||||
return rand.nextLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextLong(long lo, long hi) {
|
||||
return rand.nextLong(lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
return rand.nextInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt(int lo, int hi) {
|
||||
return rand.nextInt(lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextDouble(double lo, double hi) {
|
||||
return rand.nextDouble(lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float nextFloat(float lo, float hi) {
|
||||
return rand.nextFloat(lo, hi);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* A restrictable generator allows the creation of a new generator by restricting the range of its output values.
|
||||
* The exact semantics of this restriction depend on the concrete implementation, but it usually means taking the
|
||||
* intersection of the old range and the newly requested range of values.
|
||||
*/
|
||||
public interface RestrictableGenerator<T> extends Generator<T> {
|
||||
/**
|
||||
* Returns a new generator where the range of this generator has been restricted to the range of newLo and newHi.
|
||||
* Whether newHi is inclusive or exclusive depends on the concrete implementation.
|
||||
* @throws EmptyGeneratorException if this restriction would result in an empty generator.
|
||||
*/
|
||||
RestrictableGenerator<T> restricted(T newLo, T newHi);
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package compiler.lib.generators;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
final class RestrictableMixedGenerator<T extends Comparable<T>> extends MixedGenerator<RestrictableGenerator<T>, T> implements RestrictableGenerator<T> {
|
||||
RestrictableMixedGenerator(Generators g, List<RestrictableGenerator<T>> generators, List<Integer> weights) {
|
||||
super(g, generators, weights);
|
||||
}
|
||||
|
||||
RestrictableMixedGenerator(RestrictableMixedGenerator<T> other, T newLo, T newHi) {
|
||||
super(other, (generator) -> {
|
||||
try {
|
||||
return generator.restricted(newLo, newHi);
|
||||
} catch (EmptyGeneratorException e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestrictableGenerator<T> restricted(T newLo, T newHi) {
|
||||
return new RestrictableMixedGenerator<>(this, newLo, newHi);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
import java.util.NavigableSet;
|
||||
|
||||
/**
|
||||
* Selects values from a pre-defined list.
|
||||
*/
|
||||
final class RestrictableRandomElementGenerator<T extends Comparable<T>> extends RandomElementGenerator<T> implements RestrictableGenerator<T> {
|
||||
/*
|
||||
* Pre-generated values we can choose from. Maintained for restriction.
|
||||
*/
|
||||
private final NavigableSet<T> values;
|
||||
|
||||
public RestrictableRandomElementGenerator(Generators g, NavigableSet<T> values) {
|
||||
super(g, values);
|
||||
if (values.isEmpty()) throw new EmptyGeneratorException();
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestrictableGenerator<T> restricted(T newLo, T newHi) {
|
||||
return new RestrictableRandomElementGenerator<>(g, values.subSet(newLo, true, newHi, true));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
class RestrictableSingleValueGenerator<T extends Comparable<T>> implements RestrictableGenerator<T> {
|
||||
private final T value;
|
||||
|
||||
RestrictableSingleValueGenerator(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestrictableGenerator<T> restricted(T newLo, T newHi) {
|
||||
if (newLo.compareTo(value) <= 0 && value.compareTo(newHi) <= 0) {
|
||||
return this;
|
||||
}
|
||||
throw new EmptyGeneratorException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* A generator which always returns the same value.
|
||||
*/
|
||||
class SingleValueGenerator<T> implements Generator<T> {
|
||||
private final T value;
|
||||
|
||||
SingleValueGenerator(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* Provides a uniform double distribution random generator, in the provided range [lo, hi).
|
||||
*/
|
||||
final class UniformDoubleGenerator extends UniformIntersectionRestrictableGenerator<Double> {
|
||||
/**
|
||||
* Creates a new {@link UniformFloatGenerator}.
|
||||
*
|
||||
* @param lo Lower bound of the range (inclusive).
|
||||
* @param hi Higher bound of the range (exclusive).
|
||||
*/
|
||||
public UniformDoubleGenerator(Generators g, double lo, double hi) {
|
||||
super(g, lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double next() {
|
||||
return g.random.nextDouble(lo(), hi());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestrictableGenerator<Double> doRestrictionFromIntersection(Double lo, Double hi) {
|
||||
return new UniformDoubleGenerator(g, lo, hi);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* Provides a uniform float distribution random generator, in the provided range [lo, hi).
|
||||
*/
|
||||
final class UniformFloatGenerator extends UniformIntersectionRestrictableGenerator<Float> {
|
||||
/**
|
||||
* Creates a new {@link UniformFloatGenerator}.
|
||||
*
|
||||
* @param lo Lower bound of the range (inclusive).
|
||||
* @param hi Higher bound of the range (exclusive).
|
||||
*/
|
||||
public UniformFloatGenerator(Generators g, float lo, float hi) {
|
||||
super(g, lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float next() {
|
||||
return g.random.nextFloat(lo(), hi());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestrictableGenerator<Float> doRestrictionFromIntersection(Float lo, Float hi) {
|
||||
return new UniformFloatGenerator(g, lo, hi);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* Provides a uniform int distribution random generator.
|
||||
*/
|
||||
final class UniformIntGenerator extends UniformIntersectionRestrictableGenerator<Integer> {
|
||||
public UniformIntGenerator(Generators g, int lo, int hi) {
|
||||
super(g, lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer next() {
|
||||
if (hi() == Integer.MAX_VALUE) {
|
||||
if (lo() == Integer.MIN_VALUE) {
|
||||
return g.random.nextInt();
|
||||
}
|
||||
return g.random.nextInt(lo() - 1, hi()) + 1;
|
||||
}
|
||||
return g.random.nextInt(lo(), hi() + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestrictableGenerator<Integer> doRestrictionFromIntersection(Integer lo, Integer hi) {
|
||||
return new UniformIntGenerator(g, lo, hi);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* A generators whose outputs are restricted by taking the intersection of the previous interval and the new interval.
|
||||
*/
|
||||
abstract class UniformIntersectionRestrictableGenerator<T extends Comparable<T>> extends BoundGenerator<T> implements RestrictableGenerator<T> {
|
||||
private final T lo;
|
||||
private final T hi;
|
||||
|
||||
public UniformIntersectionRestrictableGenerator(Generators g, T lo, T hi) {
|
||||
super(g);
|
||||
if (lo.compareTo(hi) > 0) throw new EmptyGeneratorException();
|
||||
this.lo = lo;
|
||||
this.hi = hi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new generator by further restricting the range of values. The range of values will be the
|
||||
* intersection of the previous values and the values in the provided range.
|
||||
* The probability of each element occurring in the new generator stay the same relative to each other.
|
||||
*/
|
||||
@Override
|
||||
public RestrictableGenerator<T> restricted(T newLo /*as*/, T newHi /*ae*/) {
|
||||
if (lo().compareTo(newHi) > 0 || newLo.compareTo(hi()) > 0) {
|
||||
throw new EmptyGeneratorException();
|
||||
}
|
||||
return doRestrictionFromIntersection(max(newLo, lo()), min(newHi, hi()));
|
||||
}
|
||||
|
||||
private T max(T a, T b) {
|
||||
return a.compareTo(b) >= 0 ? a : b;
|
||||
}
|
||||
|
||||
private T min(T a, T b) {
|
||||
return a.compareTo(b) < 0 ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Your subclass can just override this method which will receive the computed intersection between the old and
|
||||
* new interval. It is guaranteed that the interval is non-empty.
|
||||
*/
|
||||
protected abstract RestrictableGenerator<T> doRestrictionFromIntersection(T lo, T hi);
|
||||
|
||||
T hi() { return hi; }
|
||||
T lo() { return lo; }
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.lib.generators;
|
||||
|
||||
/**
|
||||
* Provides a uniform long distribution random generator.
|
||||
*/
|
||||
final class UniformLongGenerator extends UniformIntersectionRestrictableGenerator<Long> {
|
||||
public UniformLongGenerator(Generators g, Long lo, Long hi) {
|
||||
super(g, lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long next() {
|
||||
if (hi() == Long.MAX_VALUE) {
|
||||
if (lo() == Long.MIN_VALUE) {
|
||||
return g.random.nextLong();
|
||||
}
|
||||
return g.random.nextLong(lo() - 1, hi()) + 1;
|
||||
}
|
||||
return g.random.nextLong(lo(), hi() + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestrictableGenerator<Long> doRestrictionFromIntersection(Long lo, Long hi) {
|
||||
return new UniformLongGenerator(g, lo, hi);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary An example test that shows how to use the Generators library.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @run driver testlibrary_tests.generators.tests.ExampleTest
|
||||
*/
|
||||
|
||||
package testlibrary_tests.generators.tests;
|
||||
|
||||
import compiler.lib.generators.Generator;
|
||||
|
||||
import static compiler.lib.generators.Generators.G;
|
||||
|
||||
|
||||
public class ExampleTest {
|
||||
static class FakeException extends RuntimeException {}
|
||||
|
||||
static class UnderTest {
|
||||
private enum State { STAND_BY, FIRST, SECOND };
|
||||
|
||||
private State state = State.STAND_BY;
|
||||
|
||||
void doIt(int x) {
|
||||
state = switch (state) {
|
||||
case State.STAND_BY -> x == (1 << 10) + 3 ? State.FIRST : State.STAND_BY;
|
||||
case State.FIRST -> x == (1 << 5) - 2 ? State.SECOND : State.STAND_BY;
|
||||
case State.SECOND -> {
|
||||
if (x == (1 << 4)) throw new FakeException();
|
||||
yield State.STAND_BY;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// This test should print "Assertion triggered by special" (see the math below) but almost never
|
||||
// "Assertion triggered by uniform" as the chance of triggering is about 2^-96.
|
||||
try {
|
||||
test(G.uniformInts());
|
||||
} catch (FakeException e) {
|
||||
System.out.println("Assertion triggered by uniform");
|
||||
}
|
||||
try {
|
||||
// 408 ints => 1/408 * 1/408 * 1/408 => 1/67_917_312 => with 70_000_000 loop iterations we should trigger
|
||||
test(G.powerOfTwoInts(3));
|
||||
} catch (FakeException e) {
|
||||
System.out.println("Assertion triggered by special");
|
||||
}
|
||||
}
|
||||
|
||||
public static void test(Generator<Integer> g) {
|
||||
UnderTest underTest = new UnderTest();
|
||||
for (int i = 0; i < 70_000_000 * 3; i++) {
|
||||
underTest.doIt(g.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package testlibrary_tests.generators.tests;
|
||||
|
||||
import compiler.lib.generators.RandomnessSource;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* This class is a mock for RandomnessSource. For each method defined in {@link RandomnessSource}, it maintains
|
||||
* a queue. For the methods {@link #nextInt()} and {@link #nextLong()} the queue simply contains ints and longs,
|
||||
* respectively, and they are dequeue and returned when the methods are called. For the bounded methods, each queue
|
||||
* element is a value associated with the bounds that are expected for the call. If the actual bounds do not match
|
||||
* the arguments provided, a RuntimeException is raised. This allows verifying that the correct bounds are passed to
|
||||
* the randomness source.
|
||||
* Furthermore, if a method is called and its queue is empty, an exception is raised.
|
||||
* To ensure all expected methods have been called in a test, you should call {@link #checkEmpty()} in-between tests
|
||||
* to ensure that queues are empty, that is, all expected methods have been called.
|
||||
*/
|
||||
class MockRandomnessSource implements RandomnessSource {
|
||||
private record Bounded<T>(T lo, T hi, T value) {}
|
||||
|
||||
private final Queue<Long> unboundedLongQueue = new ArrayDeque<>();
|
||||
private final Queue<Integer> unboundedIntegerQueue = new ArrayDeque<>();
|
||||
private final Queue<Bounded<Long>> boundedLongQueue = new ArrayDeque<>();
|
||||
private final Queue<Bounded<Integer>> boundedIntegerQueue = new ArrayDeque<>();
|
||||
private final Queue<Bounded<Double>> boundedDoubleQueue = new ArrayDeque<>();
|
||||
private final Queue<Bounded<Float>> boundedFloatQueue = new ArrayDeque<>();
|
||||
|
||||
private <T> T dequeueBounded(Queue<Bounded<T>> queue, T lo, T hi) {
|
||||
Bounded<T> bounded = queue.remove();
|
||||
if (!bounded.lo.equals(lo) || !bounded.hi.equals(hi)) {
|
||||
throw new RuntimeException("Expected bounds " + bounded.lo + " and " + bounded.hi + " but found " + lo + " and " + hi);
|
||||
}
|
||||
return bounded.value;
|
||||
}
|
||||
|
||||
private void checkQueueEmpty(Queue<?> queue, String name) {
|
||||
if (!queue.isEmpty()) throw new RuntimeException("Expected empty queue for " + name + " but found " + queue);
|
||||
}
|
||||
|
||||
public MockRandomnessSource enqueueLong(long value) {
|
||||
unboundedLongQueue.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockRandomnessSource enqueueInteger(int value) {
|
||||
unboundedIntegerQueue.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockRandomnessSource enqueueLong(long lo, long hi, long value) {
|
||||
boundedLongQueue.add(new Bounded<>(lo, hi, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockRandomnessSource enqueueInteger(int lo, int hi, int value) {
|
||||
boundedIntegerQueue.add(new Bounded<>(lo, hi, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockRandomnessSource enqueueDouble(double lo, double hi, double value) {
|
||||
boundedDoubleQueue.add(new Bounded<>(lo, hi, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockRandomnessSource enqueueFloat(float lo, float hi, float value) {
|
||||
boundedFloatQueue.add(new Bounded<>(lo, hi, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockRandomnessSource checkEmpty() {
|
||||
checkQueueEmpty(unboundedLongQueue, "unbounded longs");
|
||||
checkQueueEmpty(unboundedIntegerQueue, "unbounded integers");
|
||||
checkQueueEmpty(boundedLongQueue, "bounded longs");
|
||||
checkQueueEmpty(boundedIntegerQueue, "bounded integers");
|
||||
checkQueueEmpty(boundedDoubleQueue, "bounded doubles");
|
||||
checkQueueEmpty(boundedFloatQueue, "bounded floats");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextLong() {
|
||||
return unboundedLongQueue.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextLong(long lo, long hi) {
|
||||
return dequeueBounded(boundedLongQueue, lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
return unboundedIntegerQueue.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt(int lo, int hi) {
|
||||
return dequeueBounded(boundedIntegerQueue, lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextDouble(double lo, double hi) {
|
||||
return dequeueBounded(boundedDoubleQueue, lo, hi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float nextFloat(float lo, float hi) {
|
||||
return dequeueBounded(boundedFloatQueue, lo, hi);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,593 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test functionality of the Generators library.
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib /
|
||||
* @build MockRandomnessSource
|
||||
* @run driver testlibrary_tests.generators.tests.TestGenerators
|
||||
*/
|
||||
|
||||
package testlibrary_tests.generators.tests;
|
||||
|
||||
import compiler.lib.generators.EmptyGeneratorException;
|
||||
import compiler.lib.generators.Generator;
|
||||
import compiler.lib.generators.Generators;
|
||||
import compiler.lib.generators.RestrictableGenerator;
|
||||
import jdk.test.lib.Asserts;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static compiler.lib.generators.Generators.G;
|
||||
|
||||
|
||||
public class TestGenerators {
|
||||
// As it's hard to write tests with real randomness, we mock the randomness source so we can control which "random"
|
||||
// values are fed to the generators. Thus, a lot of the tests below are white-box tests, that have knowledge about
|
||||
// the internals of when randomness is consumed. There are also black-box tests which refer to Generators.G.
|
||||
// Please also see MockRandomness to learn more about this class.
|
||||
static MockRandomnessSource mockSource = new MockRandomnessSource();
|
||||
static Generators mockGS = new Generators(mockSource);
|
||||
|
||||
public static void main(String[] args) {
|
||||
testEmptyGenerators();
|
||||
testUniformInts();
|
||||
testUniformLongs();
|
||||
testAnyBits();
|
||||
testUniformFloat();
|
||||
testUniformDouble();
|
||||
testSingle();
|
||||
testMixed();
|
||||
testRandomElement();
|
||||
specialInt();
|
||||
specialLong();
|
||||
testSpecialFloat();
|
||||
testSpecialDouble();
|
||||
testSafeRestrict();
|
||||
testFill();
|
||||
testFuzzy();
|
||||
}
|
||||
|
||||
static void testMixed() {
|
||||
mockSource
|
||||
.checkEmpty()
|
||||
.enqueueInteger(0, 10, 7) // MixedGenerator chooses a generator: single
|
||||
// single was chosen but does not consume randomness
|
||||
.enqueueInteger(0, 10, 5) // MixedGenerator chooses a generator: uniform ints
|
||||
.enqueueInteger(0, 31, 4) // uniform ints samples
|
||||
.enqueueInteger(0, 10, 1) // MixedGenerator chooses a generator: uniform ints
|
||||
.enqueueInteger(0, 31, 18); // uniform ints samples
|
||||
var g0 = mockGS.mixed(mockGS.uniformInts(0, 30), mockGS.single(-1), 7, 3);
|
||||
Asserts.assertEQ(g0.next(), -1);
|
||||
Asserts.assertEQ(g0.next(), 4);
|
||||
Asserts.assertEQ(g0.next(), 18);
|
||||
|
||||
mockSource
|
||||
.checkEmpty()
|
||||
.enqueueInteger(0, 10, 1) // MixedGenerator chooses a generator: the first uniform ints
|
||||
.enqueueInteger(0, 31, 24) // uniform ints (1) samples
|
||||
.enqueueInteger(0, 10, 2) // MixedGenerator chooses a generator: single
|
||||
// single does not use randomness
|
||||
.enqueueInteger(0, 10, 7) // MixedGenerator chooses a generator: the second uniform ints
|
||||
.enqueueInteger(-10, 0, -2) // uniform ints (2) samples
|
||||
.enqueueInteger(0, 10, 9) // MixedGenerator chooses a generator: the second uniform ints
|
||||
.enqueueInteger(-10, 0, -4) // uniform ints (2) samples
|
||||
.enqueueInteger(0, 10, 1) // MixedGenerator chooses a generator: the first uniform ints
|
||||
.enqueueInteger(0, 31, 29); // uniform ints (1) samples
|
||||
|
||||
var g1 = mockGS.mixed(
|
||||
List.of(2, 5, 3),
|
||||
mockGS.uniformInts(0, 30), mockGS.single(-1), mockGS.uniformInts(-10, -1)
|
||||
);
|
||||
Asserts.assertEQ(g1.next(), 24);
|
||||
Asserts.assertEQ(g1.next(), -1);
|
||||
Asserts.assertEQ(g1.next(), -2);
|
||||
Asserts.assertEQ(g1.next(), -4);
|
||||
Asserts.assertEQ(g1.next(), 29);
|
||||
|
||||
mockSource
|
||||
.checkEmpty()
|
||||
.enqueueInteger(0, 10, 7) // MixedGenerator chooses a generator: single
|
||||
// single was chosen but does not consume randomness
|
||||
.enqueueInteger(0, 10, 5) // MixedGenerator chooses a generator: uniform ints
|
||||
.enqueueInteger(0, 21, 18); // uniform ints samples
|
||||
var g0r0 = g0.restricted(-1, 20);
|
||||
Asserts.assertEQ(g0r0.next(), -1);
|
||||
Asserts.assertEQ(g0r0.next(), 18);
|
||||
|
||||
mockSource
|
||||
.checkEmpty()
|
||||
.enqueueInteger(0, 7, 6) // MixedGenerator chooses a generator (weight for single will have been removed): uniform ints
|
||||
.enqueueInteger(4, 21, 9); // MixedGenerator chooses a generator: uniform ints
|
||||
var g0r1 = g0.restricted(4, 20);
|
||||
Asserts.assertEQ(g0r1.next(), 9);
|
||||
|
||||
mockSource
|
||||
.checkEmpty()
|
||||
.enqueueInteger(0, 10, 1) // MixedGenerator chooses a generator: the first uniform ints
|
||||
.enqueueInteger(0, 21, 2) // uniform ints (1) samples
|
||||
.enqueueInteger(0, 10, 2) // MixedGenerator chooses a generator: single
|
||||
// single does not use randomness
|
||||
.enqueueInteger(0, 10, 7) // MixedGenerator chooses a generator: the second uniform ints
|
||||
.enqueueInteger(-1, 0, -1);
|
||||
var g1r0 = g1.restricted(-1, 20);
|
||||
Asserts.assertEQ(g1r0.next(), 2);
|
||||
Asserts.assertEQ(g1r0.next(), -1);
|
||||
Asserts.assertEQ(g1r0.next(), -1);
|
||||
|
||||
mockSource
|
||||
.checkEmpty()
|
||||
.enqueueInteger(0, 10, 1) // MixedGenerator chooses a generator: the first uniform ints
|
||||
.enqueueInteger(0, 21, 2) // uniform ints (1) samples
|
||||
.enqueueInteger(0, 10, 2) // MixedGenerator chooses a generator: single
|
||||
// single does not use randomness
|
||||
.enqueueInteger(0, 10, 7) // MixedGenerator chooses a generator: the second uniform ints
|
||||
.enqueueInteger(-1, 0, -1);
|
||||
var g1r1 = g1.restricted(-1, 20);
|
||||
Asserts.assertEQ(g1r1.next(), 2);
|
||||
Asserts.assertEQ(g1r1.next(), -1);
|
||||
Asserts.assertEQ(g1r1.next(), -1);
|
||||
}
|
||||
|
||||
static void testSpecialDouble() {
|
||||
mockSource
|
||||
.checkEmpty()
|
||||
.enqueueInteger(0, 10, 3)
|
||||
.enqueueDouble(0, 1, 3.4d)
|
||||
.enqueueInteger(0, 10, 6)
|
||||
.enqueueInteger(0, 9, 1);
|
||||
var g = mockGS.mixedWithSpecialDoubles(mockGS.uniformDoubles(), 5, 5);
|
||||
Asserts.assertEQ(g.next(), 3.4d);
|
||||
Asserts.assertEQ(g.next(), -1d);
|
||||
}
|
||||
|
||||
static void testSpecialFloat() {
|
||||
mockSource
|
||||
.checkEmpty()
|
||||
.enqueueInteger(0, 10, 3)
|
||||
.enqueueFloat(0, 1, 3.4f)
|
||||
.enqueueInteger(0, 10, 6)
|
||||
.enqueueInteger(0, 9, 1);
|
||||
var g = mockGS.mixedWithSpecialFloats(mockGS.uniformFloats(), 5, 5);
|
||||
Asserts.assertEQ(g.next(), 3.4f);
|
||||
Asserts.assertEQ(g.next(), -1f);
|
||||
}
|
||||
|
||||
static void testUniformFloat() {
|
||||
mockSource.checkEmpty().enqueueFloat(-1, 10, 3.14159f);
|
||||
Asserts.assertEQ(mockGS.uniformFloats(-1, 10).next(), 3.14159f);
|
||||
mockSource.checkEmpty().enqueueFloat(0, 1, 3.14159f);
|
||||
Asserts.assertEQ(mockGS.uniformFloats(0, 1).next(), 3.14159f);
|
||||
}
|
||||
|
||||
static void testUniformDouble() {
|
||||
mockSource.checkEmpty().enqueueDouble(-1, 10, 3.14159d);
|
||||
Asserts.assertEQ(mockGS.uniformDoubles(-1, 10).next(), 3.14159d);
|
||||
mockSource.checkEmpty().enqueueDouble(0, 1, 3.14159d);
|
||||
Asserts.assertEQ(mockGS.uniformDoubles(0, 1).next(), 3.14159d);
|
||||
}
|
||||
|
||||
static void testRandomElement() {
|
||||
mockSource.checkEmpty().enqueueInteger(0, 3, 1).enqueueInteger(0, 3, 0);
|
||||
var g = mockGS.randomElement(List.of("a", "b", "c"));
|
||||
Asserts.assertEQ(g.next(), "b");
|
||||
Asserts.assertEQ(g.next(), "a");
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(0, 8, 1).enqueueInteger(0, 8, 2);
|
||||
// The list below is intentionally not sorted and is equivalent to: 1, 4, 4, 8, 9, 10, 13, 18, 20
|
||||
// It contains 8 distinct values. Note that orderedRandomElement removes duplicates. Therefore the internal
|
||||
// value list is: 1, 4, 8, 9, 10, 13, 18, 20
|
||||
var g1 = mockGS.orderedRandomElement(List.of(10, 4, 1, 8, 9, 4, 20, 18, 13));
|
||||
Asserts.assertEQ(g1.next(), 4);
|
||||
Asserts.assertEQ(g1.next(), 8);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(0, 3, 1).enqueueInteger(0, 3, 2);
|
||||
// Ordered lists can also be restricted. Our new values are 9, 10, 13.
|
||||
var gr = g1.restricted(9, 13);
|
||||
Asserts.assertEQ(gr.next(), 10);
|
||||
Asserts.assertEQ(gr.next(), 13);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(0, 2, 1);
|
||||
var gs = mockGS.orderedRandomElement(List.of("Bob", "Alice", "Carol")).restricted("Al", "Bz");
|
||||
Asserts.assertEQ(gs.next(), "Bob");
|
||||
}
|
||||
|
||||
static void specialInt() {
|
||||
mockSource.checkEmpty().enqueueInteger(0, 63, 1).enqueueInteger(0, 63, 32);
|
||||
var si = mockGS.powerOfTwoInts(0);
|
||||
Asserts.assertEQ(si.next(), -(1 << 30));
|
||||
Asserts.assertEQ(si.next(), 1);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(0, 182, 1);
|
||||
var si1 = mockGS.powerOfTwoInts(1);
|
||||
Asserts.assertEQ(si1.next(), -(1 << 31) + 1);
|
||||
}
|
||||
|
||||
static void specialLong() {
|
||||
mockSource.checkEmpty().enqueueInteger(0, 127, 1).enqueueInteger(0, 127, 64);
|
||||
var si = mockGS.powerOfTwoLongs(0);
|
||||
Asserts.assertEQ(si.next(), -(1L << 62));
|
||||
Asserts.assertEQ(si.next(), 1L);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(0, 374, 1);
|
||||
var si1 = mockGS.powerOfTwoLongs(1);
|
||||
Asserts.assertEQ(si1.next(), -(1L << 63) + 1);
|
||||
}
|
||||
|
||||
static void testSingle() {
|
||||
mockSource.checkEmpty();
|
||||
var g = mockGS.single(30);
|
||||
Asserts.assertEQ(g.next(), 30);
|
||||
Asserts.assertEQ(g.next(), 30);
|
||||
Asserts.assertEQ(g.restricted(10, 50).next(), 30);
|
||||
var gs = mockGS.single("hello");
|
||||
Asserts.assertEQ(gs.next(), "hello");
|
||||
Asserts.assertEQ(gs.next(), "hello");
|
||||
Asserts.assertEQ(gs.restricted("a", "q").next(), "hello");
|
||||
var theObject = new Object();
|
||||
var go = mockGS.single(theObject);
|
||||
Asserts.assertEQ(go.next(), theObject);
|
||||
Asserts.assertEQ(go.next(), theObject);
|
||||
}
|
||||
|
||||
static void testUniformInts() {
|
||||
mockSource.checkEmpty().enqueueInteger(0, 11, 1).enqueueInteger(0, 11, 4);
|
||||
var g0 = mockGS.uniformInts(0, 10);
|
||||
Asserts.assertEQ(g0.next(), 1);
|
||||
Asserts.assertEQ(g0.next(), 4);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(0, 1, 0).enqueueInteger(0, 1, 0);
|
||||
var g1 = mockGS.uniformInts(0, 0);
|
||||
Asserts.assertEQ(g1.next(), 0);
|
||||
Asserts.assertEQ(g1.next(), 0);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(-1, Integer.MAX_VALUE, 10);
|
||||
Asserts.assertEQ(mockGS.uniformInts(0, Integer.MAX_VALUE).next(), 11);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(Integer.MIN_VALUE, 13, -33);
|
||||
Asserts.assertEQ(mockGS.uniformInts(Integer.MIN_VALUE, 12).next(), -33);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(11);
|
||||
Asserts.assertEQ(mockGS.uniformInts(Integer.MIN_VALUE, Integer.MAX_VALUE).next(), 11);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(10, 29, 17);
|
||||
Asserts.assertEQ(mockGS.uniformInts(Integer.MIN_VALUE, Integer.MAX_VALUE).restricted(10, 28).next(), 17);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(19, 29, 17);
|
||||
Asserts.assertEQ(mockGS.uniformInts(Integer.MIN_VALUE, Integer.MAX_VALUE).restricted(10, 28).restricted(19, 33).next(), 17);
|
||||
|
||||
// inside interval positive
|
||||
mockSource.checkEmpty().enqueueInteger(12, 19, 17);
|
||||
Asserts.assertEQ(mockGS.uniformInts(10, 20).restricted(12, 18).next(), 17);
|
||||
|
||||
// inside interval negative
|
||||
mockSource.checkEmpty().enqueueInteger(-18, -11, -17);
|
||||
Asserts.assertEQ(mockGS.uniformInts(-20, -10).restricted(-18, -12).next(), -17);
|
||||
|
||||
// left interval positive
|
||||
mockSource.checkEmpty().enqueueInteger(10, 13, 11);
|
||||
Asserts.assertEQ(mockGS.uniformInts(10, 20).restricted(5, 12).next(), 11);
|
||||
|
||||
// left interval negative
|
||||
mockSource.checkEmpty().enqueueInteger(-12, -9, -11);
|
||||
Asserts.assertEQ(mockGS.uniformInts(-20, -10).restricted(-12, -5).next(), -11);
|
||||
|
||||
// right interval positive
|
||||
mockSource.checkEmpty().enqueueInteger(17, 21, 19);
|
||||
Asserts.assertEQ(mockGS.uniformInts(10, 20).restricted(17, 22).next(), 19);
|
||||
|
||||
// right interval negative
|
||||
mockSource.checkEmpty().enqueueInteger(-20, -16, -19);
|
||||
Asserts.assertEQ(mockGS.uniformInts(-20, -10).restricted(-22, -17).next(), -19);
|
||||
|
||||
mockSource.checkEmpty().enqueueInteger(144);
|
||||
Asserts.assertEQ(mockGS.uniformInts().next(), 144);
|
||||
|
||||
mockSource.checkEmpty();
|
||||
}
|
||||
|
||||
static void testUniformLongs() {
|
||||
mockSource.checkEmpty().enqueueLong(0, 11, 1).enqueueLong(0, 11, 4);
|
||||
var g0 = mockGS.uniformLongs(0, 10);
|
||||
Asserts.assertEQ(g0.next(), 1L);
|
||||
Asserts.assertEQ(g0.next(), 4L);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(0, 1, 0).enqueueLong(0, 1, 0);
|
||||
var g1 = mockGS.uniformLongs(0, 0);
|
||||
Asserts.assertEQ(g1.next(), 0L);
|
||||
Asserts.assertEQ(g1.next(), 0L);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(-1, Long.MAX_VALUE, 10);
|
||||
Asserts.assertEQ(mockGS.uniformLongs(0, Long.MAX_VALUE).next(), 11L);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(Long.MIN_VALUE, 13, -33);
|
||||
Asserts.assertEQ(mockGS.uniformLongs(Long.MIN_VALUE, 12).next(), -33L);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(11);
|
||||
Asserts.assertEQ(mockGS.uniformLongs(Long.MIN_VALUE, Long.MAX_VALUE).next(), 11L);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(10, 29, 17);
|
||||
Asserts.assertEQ(mockGS.uniformLongs(Long.MIN_VALUE, Long.MAX_VALUE).restricted(10L, 28L).next(), 17L);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(19, 29, 17);
|
||||
Asserts.assertEQ(mockGS.uniformLongs(Long.MIN_VALUE, Long.MAX_VALUE).restricted(10L, 28L).restricted(19L, 33L).next(), 17L);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(144);
|
||||
Asserts.assertEQ(mockGS.uniformLongs().next(), 144L);
|
||||
|
||||
mockSource.checkEmpty();
|
||||
}
|
||||
|
||||
static void testAnyBits() {
|
||||
mockSource.checkEmpty().enqueueInteger(Float.floatToIntBits(3.14159f));
|
||||
Asserts.assertEQ(mockGS.anyBitsFloats().next(), 3.14159f);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(Double.doubleToLongBits(3.14159d));
|
||||
Asserts.assertEQ(mockGS.anyBitsDouble().next(), 3.14159d);
|
||||
}
|
||||
|
||||
static void testEmptyGenerators() {
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformInts(1, 0));
|
||||
Asserts.assertNotNull(G.uniformInts(0, 0));
|
||||
Asserts.assertNotNull(G.uniformInts(0, 1));
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformInts(0, 1).restricted(2, 5));
|
||||
Asserts.assertNotNull(G.uniformInts(0, 1).restricted(0, 1));
|
||||
Asserts.assertNotNull(G.uniformInts(0, 1).restricted(1, 5));
|
||||
Asserts.assertNotNull(G.uniformInts(0, 10).restricted(1, 2));
|
||||
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformLongs(1, 0));
|
||||
Asserts.assertNotNull(G.uniformLongs(0, 0));
|
||||
Asserts.assertNotNull(G.uniformLongs(0, 1));
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformLongs(0, 1).restricted(2L, 5L));
|
||||
Asserts.assertNotNull(G.uniformLongs(0, 1).restricted(0L, 1L));
|
||||
Asserts.assertNotNull(G.uniformLongs(0, 1).restricted(1L, 5L));
|
||||
Asserts.assertNotNull(G.uniformLongs(0, 10).restricted(1L, 2L));
|
||||
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformDoubles(1, 0));
|
||||
Asserts.assertNotNull(G.uniformDoubles(0, 1));
|
||||
Asserts.assertNotNull(G.uniformDoubles(0, 0));
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformDoubles(0, 1).restricted(1.1d, 2.4d));
|
||||
Asserts.assertNotNull(G.uniformDoubles(0, 1).restricted(0.9d, 2.4d));
|
||||
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformFloats(1, 0));
|
||||
Asserts.assertNotNull(G.uniformFloats(0, 1));
|
||||
Asserts.assertNotNull(G.uniformFloats(0, 0));
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.uniformFloats(0, 1).restricted(1.1f, 2.4f));
|
||||
Asserts.assertNotNull(G.uniformFloats(0, 1).restricted(0.9f, 2.4f));
|
||||
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.randomElement(List.of()));
|
||||
Asserts.assertNotNull(G.randomElement(List.of("a", "b", "c")));
|
||||
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.orderedRandomElement(new ArrayList<Integer>()));
|
||||
Asserts.assertNotNull(G.orderedRandomElement(List.of(48, 29, 17)));
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.orderedRandomElement(List.of(48, 29, 17)).restricted(-12, 10));
|
||||
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.single(10).restricted(0, 1));
|
||||
Asserts.assertNotNull(G.single(10).restricted(9, 10));
|
||||
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.mixed(G.uniformInts(0, 10), G.uniformInts(15, 20), 5, 5).restricted(30, 34));
|
||||
Asserts.assertNotNull(G.mixed(G.uniformInts(0, 10), G.uniformInts(15, 20), 5, 5).restricted(5, 18));
|
||||
Asserts.assertNotNull(G.mixed(G.uniformInts(0, 10), G.uniformInts(15, 20), 5, 5).restricted(5, 7));
|
||||
Asserts.assertNotNull(G.mixed(G.uniformInts(0, 10), G.uniformInts(15, 20), 5, 5).restricted(16, 18));
|
||||
|
||||
Asserts.assertThrows(EmptyGeneratorException.class, () -> G.mixed(
|
||||
List.of(3, 4, 6),
|
||||
G.uniformInts(0, 10), G.uniformInts(15, 20), G.uniformInts(30, 40)
|
||||
).restricted(80, 83));
|
||||
Asserts.assertNotNull(G.mixed(
|
||||
List.of(3, 4, 6),
|
||||
G.uniformInts(0, 10), G.uniformInts(15, 20), G.uniformInts(30, 40)
|
||||
).restricted(10, 35));
|
||||
Asserts.assertNotNull(G.mixed(
|
||||
List.of(3, 4, 6),
|
||||
G.uniformInts(0, 10), G.uniformInts(15, 20), G.uniformInts(30, 40)
|
||||
).restricted(5, 8));
|
||||
Asserts.assertNotNull(G.mixed(
|
||||
List.of(3, 4, 6),
|
||||
G.uniformInts(0, 10), G.uniformInts(15, 20), G.uniformInts(30, 40)
|
||||
).restricted(17, 19));
|
||||
Asserts.assertNotNull(G.mixed(
|
||||
List.of(3, 4, 6),
|
||||
G.uniformInts(0, 10), G.uniformInts(15, 20), G.uniformInts(30, 40)
|
||||
).restricted(31, 38));
|
||||
}
|
||||
|
||||
static void testSafeRestrict() {
|
||||
// normal restrictions
|
||||
mockSource.checkEmpty().enqueueInteger(4, 6, 4);
|
||||
var g1 = mockGS.safeRestrict(mockGS.uniformInts(4, 5), 2, 5);
|
||||
Asserts.assertEQ(g1.next(), 4);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(4, 6, 4);
|
||||
var g2 = mockGS.safeRestrict(mockGS.uniformLongs(4, 5), 2, 5);
|
||||
Asserts.assertEQ(g2.next(), 4L);
|
||||
|
||||
mockSource.checkEmpty().enqueueDouble(4, 5, 4);
|
||||
var g3 = mockGS.safeRestrict(mockGS.uniformDoubles(4, 5), 2, 5);
|
||||
Asserts.assertEQ(g3.next(), 4d);
|
||||
|
||||
mockSource.checkEmpty().enqueueFloat(4, 5, 4);
|
||||
var g4 = mockGS.safeRestrict(mockGS.uniformFloats(4, 5), 2, 5);
|
||||
Asserts.assertEQ(g4.next(), 4f);
|
||||
|
||||
// fallbacks
|
||||
mockSource.checkEmpty().enqueueInteger(2, 6, 4);
|
||||
var f1 = mockGS.safeRestrict(mockGS.uniformInts(0, 1), 2, 5);
|
||||
Asserts.assertEQ(f1.next(), 4);
|
||||
|
||||
mockSource.checkEmpty().enqueueLong(2, 6, 4);
|
||||
var f2 = mockGS.safeRestrict(mockGS.uniformLongs(0, 1), 2, 5);
|
||||
Asserts.assertEQ(f2.next(), 4L);
|
||||
|
||||
mockSource.checkEmpty().enqueueDouble(2, 5, 4);
|
||||
var f3 = mockGS.safeRestrict(mockGS.uniformDoubles(0, 1), 2, 5);
|
||||
Asserts.assertEQ(f3.next(), 4d);
|
||||
|
||||
mockSource.checkEmpty().enqueueFloat(2, 5, 4);
|
||||
var f4 = mockGS.safeRestrict(mockGS.uniformFloats(0, 1), 2, 5);
|
||||
Asserts.assertEQ(f4.next(), 4f);
|
||||
}
|
||||
|
||||
static void testFill() {
|
||||
// All we need to test really is that fill calls the generators sequentially and correctly writes the values
|
||||
// into the arrays. Since fill with arrays uses memory segments internally, these are also tested.
|
||||
|
||||
Generator<Double> doubleGen = new Generator<>() {
|
||||
private double i = 1;
|
||||
|
||||
@Override
|
||||
public Double next() {
|
||||
i /= 2;
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
double[] doubles = new double[5];
|
||||
mockGS.fill(doubleGen, doubles);
|
||||
Asserts.assertTrue(Arrays.equals(doubles, (new double[] {0.5, 0.25, 0.125, 0.0625, 0.03125})));
|
||||
|
||||
Generator<Float> floatGen = new Generator<>() {
|
||||
private float i = 1;
|
||||
|
||||
@Override
|
||||
public Float next() {
|
||||
i /= 2;
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
float[] floats = new float[5];
|
||||
mockGS.fill(floatGen, floats);
|
||||
Asserts.assertTrue(Arrays.equals(floats, (new float[] {0.5f, 0.25f, 0.125f, 0.0625f, 0.03125f})));
|
||||
|
||||
Generator<Long> longGen = new Generator<>() {
|
||||
private long i = 1;
|
||||
|
||||
@Override
|
||||
public Long next() {
|
||||
return i++;
|
||||
}
|
||||
};
|
||||
|
||||
long[] longs = new long[5];
|
||||
mockGS.fill(longGen, longs);
|
||||
Asserts.assertTrue(Arrays.equals(longs, (new long[] {1, 2, 3, 4, 5})));
|
||||
|
||||
Generator<Integer> intGen = new Generator<>() {
|
||||
private int i = 1;
|
||||
|
||||
@Override
|
||||
public Integer next() {
|
||||
return i++;
|
||||
}
|
||||
};
|
||||
|
||||
int[] ints = new int[5];
|
||||
mockGS.fill(intGen, ints);
|
||||
Asserts.assertTrue(Arrays.equals(ints, (new int[] {1, 2, 3, 4, 5})));
|
||||
}
|
||||
|
||||
static void testFuzzy() {
|
||||
var intBoundGen = G.uniformInts();
|
||||
for (int j = 0; j < 500; j++) {
|
||||
int a = intBoundGen.next(), b = intBoundGen.next();
|
||||
int lo = Math.min(a, b), hi = Math.max(a, b);
|
||||
RestrictableGenerator<Integer> gb;
|
||||
try {
|
||||
gb = G.ints().restricted(lo, hi);
|
||||
} catch (EmptyGeneratorException e) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < 10_000; i++) {
|
||||
int x = gb.next();
|
||||
Asserts.assertGreaterThanOrEqual(x, lo);
|
||||
Asserts.assertLessThanOrEqual(x, hi);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < 500; j++) {
|
||||
int a = intBoundGen.next(), b = intBoundGen.next();
|
||||
int lo = Math.min(a, b), hi = Math.max(a, b);
|
||||
var gb = G.uniformInts(lo, hi);
|
||||
for (int i = 0; i < 10_000; i++) {
|
||||
int x = gb.next();
|
||||
Asserts.assertGreaterThanOrEqual(x, lo);
|
||||
Asserts.assertLessThanOrEqual(x, hi);
|
||||
}
|
||||
}
|
||||
|
||||
var longBoundGen = G.uniformLongs();
|
||||
for (int j = 0; j < 500; j++) {
|
||||
long a = longBoundGen.next(), b = longBoundGen.next();
|
||||
long lo = Math.min(a, b), hi = Math.max(a, b);
|
||||
RestrictableGenerator<Long> gb;
|
||||
try {
|
||||
gb = G.longs().restricted(lo, hi);
|
||||
} catch (EmptyGeneratorException e) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < 10_000; i++) {
|
||||
long x = gb.next();
|
||||
Asserts.assertGreaterThanOrEqual(x, lo);
|
||||
Asserts.assertLessThanOrEqual(x, hi);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < 500; j++) {
|
||||
long a = longBoundGen.next(), b = longBoundGen.next();
|
||||
long lo = Math.min(a, b), hi = Math.max(a, b);
|
||||
var gb = G.uniformLongs(lo, hi);
|
||||
for (int i = 0; i < 10_000; i++) {
|
||||
long x = gb.next();
|
||||
Asserts.assertGreaterThanOrEqual(x, lo);
|
||||
Asserts.assertLessThanOrEqual(x, hi);
|
||||
}
|
||||
}
|
||||
|
||||
var floatBoundGen = G.uniformFloats();
|
||||
for (int j = 0; j < 500; j++) {
|
||||
float a = floatBoundGen.next(), b = floatBoundGen.next();
|
||||
float lo = Math.min(a, b), hi = Math.max(a, b);
|
||||
var gb = G.uniformFloats(lo, hi);
|
||||
for (int i = 0; i < 10_000; i++) {
|
||||
float x = gb.next();
|
||||
Asserts.assertGreaterThanOrEqual(x, lo);
|
||||
Asserts.assertLessThan(x, hi);
|
||||
}
|
||||
}
|
||||
|
||||
var doubleBoundGen = G.uniformDoubles();
|
||||
for (int j = 0; j < 500; j++) {
|
||||
double a = doubleBoundGen.next(), b = doubleBoundGen.next();
|
||||
double lo = Math.min(a, b), hi = Math.max(a, b);
|
||||
var gb = G.uniformDoubles(lo, hi);
|
||||
for (int i = 0; i < 10_000; i++) {
|
||||
double x = gb.next();
|
||||
Asserts.assertGreaterThanOrEqual(x, lo);
|
||||
Asserts.assertLessThan(x, hi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user