All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jqwik.engine.properties.arbitraries.randomized.RandomGenerators Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
package net.jqwik.engine.properties.arbitraries.randomized;

import java.math.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;

import net.jqwik.api.*;
import net.jqwik.engine.properties.arbitraries.*;
import net.jqwik.engine.properties.shrinking.*;

public class RandomGenerators {

	public static final int DEFAULT_COLLECTION_SIZE = 255;

	public static  RandomGenerator choose(List values) {
		if (values.size() == 0) {
			return fail("empty set of values");
		}
		return random -> {
			U value = chooseValue(values, random);
			return new ChooseValueShrinkable<>(value, values);
		};
	}

	private static  U chooseValue(List values, Random random) {
		int index = random.nextInt(values.size());
		return values.get(index);
	}

	public static  RandomGenerator choose(U[] values) {
		return choose(Arrays.asList(values));
	}

	public static > RandomGenerator choose(Class enumClass) {
		return choose(enumClass.getEnumConstants());
	}

	public static RandomGenerator choose(char[] characters) {
		List validCharacters = new ArrayList<>(characters.length);
		for (char character : characters) {
			validCharacters.add(character);
		}
		return choose(validCharacters);
	}

	public static RandomGenerator chars(char min, char max) {
		return integers(min, max).map(anInt -> ((char) (int) anInt));
	}

	public static RandomGenerator bytes(byte min, byte max) {
		BigInteger min1 = BigInteger.valueOf(min);
		BigInteger max1 = BigInteger.valueOf(max);
		return bigIntegers(min1, max1, defaultShrinkingTargetCalculator(min1, max1)).map(BigInteger::byteValueExact);
	}

	public static RandomGenerator shorts(short min, short max) {
		BigInteger min1 = BigInteger.valueOf(min);
		BigInteger max1 = BigInteger.valueOf(max);
		return bigIntegers(min1, max1, defaultShrinkingTargetCalculator(min1, max1)).map(BigInteger::shortValueExact);
	}

	public static RandomGenerator integers(int min, int max) {
		BigInteger min1 = BigInteger.valueOf(min);
		BigInteger max1 = BigInteger.valueOf(max);
		return bigIntegers(min1, max1, defaultShrinkingTargetCalculator(min1, max1)).map(BigInteger::intValueExact);
	}

	public static RandomGenerator longs(long min, long max) {
		BigInteger min1 = BigInteger.valueOf(min);
		BigInteger max1 = BigInteger.valueOf(max);
		return bigIntegers(min1, max1, defaultShrinkingTargetCalculator(min1, max1)).map(BigInteger::longValueExact);
	}

	public static RandomGenerator bigIntegers(
		BigInteger min,
		BigInteger max,
		Function shrinkingTargetCalculator,
		BigInteger... partitionPoints
	) {
		Range range = Range.of(min, max);
		return RandomIntegralGenerators.bigIntegers(range, partitionPoints, shrinkingTargetCalculator);
	}

	public static RandomGenerator doubles(double min, double max, int scale) {
		return bigDecimals(BigDecimal.valueOf(min), BigDecimal.valueOf(max), scale).map(BigDecimal::doubleValue);
	}

	public static RandomGenerator floats(float min, float max, int scale) {
		return bigDecimals(BigDecimal.valueOf((double) min), BigDecimal.valueOf((double) max), scale).map(BigDecimal::floatValue);
	}

	public static RandomGenerator bigDecimals(BigDecimal min, BigDecimal max, int scale, BigDecimal... partitionPoints) {
		return RandomDecimalGenerators.bigDecimals(Range.of(min, max), scale, partitionPoints);
	}

	public static  RandomGenerator> list(RandomGenerator elementGenerator, int minSize, int maxSize) {
		int defaultCutoff = defaultCutoffSize(minSize, maxSize);
		return list(elementGenerator, minSize, maxSize, defaultCutoff);
	}

	public static  RandomGenerator> list(
		RandomGenerator elementGenerator, int minSize, int maxSize, int cutoffSize
	) {
		Function>, Shrinkable>> createShrinkable = elements -> new ShrinkableList<>(elements, minSize);
		return container(elementGenerator, createShrinkable, minSize, maxSize, cutoffSize);
	}

	public static RandomGenerator oneOf(List> all) {
		return choose(all).flatMap(Function.identity());
	}

	public static  RandomGenerator> shuffle(List values) {
		return random -> {
			List clone = new ArrayList<>(values);
			Collections.shuffle(clone, random);
			return Shrinkable.unshrinkable(clone);
		};
	}

	public static RandomGenerator strings(
		RandomGenerator elementGenerator, int minLength, int maxLength, int cutoffLength
	) {
		Function>, Shrinkable> createShrinkable = elements -> new ShrinkableString(elements, minLength);
		return container(elementGenerator, createShrinkable, minLength, maxLength, cutoffLength);
	}

	public static RandomGenerator strings(
		RandomGenerator elementGenerator, int minLength, int maxLength
	) {
		int defaultCutoff = defaultCutoffSize(minLength, maxLength);
		return strings(elementGenerator, minLength, maxLength, defaultCutoff);
	}

	private static int defaultCutoffSize(int minSize, int maxSize) {
		int range = maxSize - minSize;
		int offset = (int) Math.max(Math.round(Math.sqrt(100)), 10);
		if (range <= offset)
			return maxSize;
		return Math.min(offset + minSize, maxSize);
	}

	private static  RandomGenerator container(
		RandomGenerator elementGenerator, //
		Function>, Shrinkable> createShrinkable,//
		int minSize, int maxSize, int cutoffSize
	) {
		Function sizeGenerator = sizeGenerator(minSize, maxSize, cutoffSize);
		return new ContainerGenerator<>(elementGenerator, createShrinkable, sizeGenerator);
	}

	private static Function sizeGenerator(int minSize, int maxSize, int cutoffSize) {
		if (cutoffSize >= maxSize)
			return random -> randomSize(random, minSize, maxSize);
		// Choose size below cutoffSize with probability of 0.9
		return random -> {
			if (random.nextDouble() > 0.1)
				return randomSize(random, minSize, cutoffSize);
			else
				return randomSize(random, cutoffSize + 1, maxSize);
		};
	}

	private static int randomSize(Random random, int minSize, int maxSize) {
		int range = maxSize - minSize;
		return random.nextInt(range + 1) + minSize;
	}

	public static  RandomGenerator> set(RandomGenerator elementGenerator, int minSize, int maxSize) {
		int defaultCutoffSize = defaultCutoffSize(minSize, maxSize);
		return set(elementGenerator, minSize, maxSize, defaultCutoffSize);
	}

	public static  RandomGenerator> set(
		RandomGenerator elementGenerator, int minSize, int maxSize, int cutoffSize
	) {
		Function sizeGenerator = sizeGenerator(minSize, maxSize, cutoffSize);
		return random -> {
			int listSize = sizeGenerator.apply(random);
			Set> elements = new HashSet<>();
			Set values = new HashSet<>();
			while (elements.size() < listSize) {
				Shrinkable next = elementGenerator.next(random);
				if (values.contains(next.value()))
					continue;
				elements.add(next);
				values.add(next.value());
			}
			return new ShrinkableSet<>(elements, minSize);
		};
	}

	public static  RandomGenerator chooseShrinkable(List> shrinkables) {
		if (shrinkables.size() == 0) {
			return fail("empty set of shrinkables");
		}
		return random -> chooseValue(shrinkables, random);
	}

	public static  RandomGenerator samplesFromShrinkables(List> samples) {
		AtomicInteger tryCount = new AtomicInteger(0);
		return ignored -> {
			if (tryCount.get() >= samples.size())
				tryCount.set(0);
			return samples.get(tryCount.getAndIncrement());
		};
	}

	public static  RandomGenerator samples(T[] samples) {
		List> shrinkables = SampleShrinkable.listOf(samples);
		return samplesFromShrinkables(shrinkables);
	}

	public static  RandomGenerator frequency(List> frequencies) {
		return new FrequencyGenerator<>(frequencies);
	}


	public static  RandomGenerator withEdgeCases(RandomGenerator self, int genSize, List> edgeCases) {
		if (edgeCases.isEmpty()) {
			return self;
		}

		int baseToEdgeCaseRatio =
			Math.min(
				Math.max(Math.round(genSize / 5), 1),
				100 / edgeCases.size()
			) + 1;

		RandomGenerator edgeCasesGenerator = RandomGenerators.chooseShrinkable(edgeCases);

		return random -> {
			if (random.nextInt(baseToEdgeCaseRatio) == 0) {
				return edgeCasesGenerator.next(random);
			} else {
				return self.next(random);
			}
		};
	}


	public static  RandomGenerator fail(String message) {
		return ignored -> {
			throw new JqwikException(message);
		};
	}

	public static int defaultCutoffSize(int minSize, int maxSize, int genSize) {
		int range = maxSize - minSize;
		int offset = (int) Math.max(Math.round(Math.sqrt(genSize)), 10);
		if (range <= offset)
			return maxSize;
		return Math.min(offset + minSize, maxSize);
	}

	public static Function defaultShrinkingTargetCalculator(BigInteger min, BigInteger max) {
		return value -> ShrinkableBigInteger.defaultShrinkingTarget(value, Range.of(min, max));
	}

	// TODO: This could be way more sophisticated
	public static BigInteger[] calculateDefaultPartitionPoints(int tries, BigInteger min, BigInteger max) {
		int partitionPoint = Math.max(tries / 2, 10);
		BigInteger upperPartitionPoint = BigInteger.valueOf(partitionPoint).min(max);
		BigInteger lowerPartitionPoint = BigInteger.valueOf(partitionPoint).negate().max(min);
		return new BigInteger[]{lowerPartitionPoint, upperPartitionPoint};
	}
}