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

net.jqwik.engine.support.ChooseRandomlyByFrequency Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
package net.jqwik.engine.support;

import java.util.*;
import java.util.function.*;

import net.jqwik.api.*;

import org.jspecify.annotations.*;

public class ChooseRandomlyByFrequency implements Function {

	private int[] upperBounds;
	private int size = 0;
	private List valuesToChooseFrom;

	public ChooseRandomlyByFrequency(List> frequencies) {
		calculateUpperBorders(frequencies);
		if (size <= 0) {
			throw new JqwikException(String.format(
				"%s does not contain any positive frequencies.",
				JqwikStringSupport.displayString(frequencies)
			));
		}
	}

	protected List possibleValues() {
		return valuesToChooseFrom;
	}

	private void calculateUpperBorders(List> frequencies) {
		List values = new ArrayList<>(frequencies.size());
		// Zero-frequency elements are possible, so we start with a list, and transform to array later
		List upperBounds = new ArrayList<>(frequencies.size());
		for (Tuple.Tuple2 tuple : frequencies) {
			int frequency = tuple.get1();
			if (frequency <= 0)
				continue;
			size = Math.addExact(size, frequency);
			T value = tuple.get2();
			values.add(value);
			upperBounds.add(size);
		}
		valuesToChooseFrom = values;
		this.upperBounds = upperBounds.stream().mapToInt(i -> i).toArray();
	}

	private T choose(int index) {
		int i = Arrays.binarySearch(upperBounds, index);
		if (i < 0) {
			// Exact value not found => convert "negative" insertion point to the actual index
			i = -(i + 1);
		} else {
			// Exact value is found, use the next bucket
			// For instance, if weights are {2,3}, the upperBounds are {2,5},
			// and the input indices could be 0..4.
			// "index" {0, 1} should be mapped to the first element
			// "index" {2, 3, 4} should be mapped to the second
			// If (input) index==2, binarySearch returns 0, so we must advance i by one
			// This advance is never triggered for the very last element since random.nextInt
			// excludes upper bound.
			i++;
		}
		return valuesToChooseFrom.get(i);
	}

	public T apply(Random random) {
		return choose(random.nextInt(size));
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy