/* * 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. *

* Normally, clients get the default Generators instance by referring to the static variable {@link #G}. *

* 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. *

* Many distributions are restrictable. 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 h(int, int, int) 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: * *


 * void test(Generator{@literal } g) {
 *     h(g.restricted(1, 10).next(), g.next(), g.restricted(-10, 100).next());
 * }
 * 
* * Then test can be called with different distributions, for example: * *

 * test(G.uniformInts());
 * test(G.specialInts(0));
 * 
*

* 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: * *


 * test(G.single(1));
 * 
* *

* 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. *

* 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: *

G.mixed(G.single(true), G.single(false), 60, 40)
*

* 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: *

G.orderedRandomElement(List.of("Bob", "Alice", "Carol")).restricted("Al", "Bz")
* 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. *

* 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. *

* 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 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 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 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 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 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 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 anyBitsDouble() { return new AnyBitsDoubleGenerator(this); } /** * Generates uniform doubles in the range of [lo, hi) (inclusive of lo, exclusive of hi). */ public RestrictableGenerator 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 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 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 Generator randomElement(Collection 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 > RestrictableGenerator orderedRandomElement(Collection list) { NavigableSet set = list instanceof NavigableSet ? (NavigableSet) list : new TreeSet<>(list); return new RestrictableRandomElementGenerator<>(this, set); } /** * Returns a generator that always generate the provided value. */ public Generator single(T value) { return new SingleValueGenerator<>(value); } /** * Returns a restrictable generator that always generate the provided value. */ public > RestrictableGenerator 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 Generator mixed(Generator a, Generator 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 Generator mixed(List weights, Generator... 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 > RestrictableGenerator mixed(RestrictableGenerator a, RestrictableGenerator 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 > RestrictableGenerator mixed(List weights, RestrictableGenerator... generators) { return new RestrictableMixedGenerator<>(this, Arrays.asList(generators), weights); } /** * Randomly pick an int generator. * * @return Random int generator. */ public RestrictableGenerator 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 powerOfTwoInts(int range) { TreeSet 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 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 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 powerOfTwoLongs(int range) { TreeSet 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 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 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 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 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 mixedWithSpecialDoubles(Generator 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 mixedWithSpecialDoubles(RestrictableGenerator 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 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 mixedWithSpecialFloats(Generator 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 mixedWithSpecialFloats(RestrictableGenerator 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 safeRestrict(RestrictableGenerator 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 safeRestrict(RestrictableGenerator 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 safeRestrict(RestrictableGenerator 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 safeRestrict(RestrictableGenerator 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 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 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 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 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 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 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 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 generator, long[] a) { fillLong(generator, MemorySegment.ofArray(a)); } }