Randomness.java
/*
* Copyright (c) 2021 Mārtiņš Avots (Martins Avots) and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0, or the MIT License,
* which is available at https://spdx.org/licenses/MIT.html.
*
* SPDX-License-Identifier: EPL-2.0 OR MIT
*/
package net.splitcells.dem.utils.random;
import net.splitcells.dem.data.set.list.List;
import net.splitcells.dem.lang.annotations.JavaLegacyArtifact;
import net.splitcells.dem.utils.MathUtils;
import java.util.Random;
import static java.lang.Math.abs;
import static java.util.stream.IntStream.range;
import static net.splitcells.dem.data.set.Sets.setOfUniques;
import static net.splitcells.dem.data.set.list.Lists.list;
import static net.splitcells.dem.data.set.list.Lists.listWithValuesOf;
import static net.splitcells.dem.environment.config.StaticFlags.ENFORCING_UNIT_CONSISTENCY;
import static net.splitcells.dem.utils.MathUtils.*;
import static org.assertj.core.api.Assertions.assertThat;
public interface Randomness {
default int integer() {
return integer(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
int integer(final Integer min, final Integer max);
default int integer(int min, double mean, int max) {
if (ENFORCING_UNIT_CONSISTENCY) {
assertThat(intToDouble(min))
.describedAs("min: " + min + " mean: " + mean + " max: " + max)
.isLessThanOrEqualTo(mean);
assertThat(mean)
.describedAs("min: " + min + " mean: " + mean + " max: " + max)
.isLessThanOrEqualTo(max);
}
final var distance = distance(min, max);
final var distanceHalf = roundToInt(distance / 2d);
if (truthValue((mean) / MathUtils.intToDouble(max))) {
return integer(min + distanceHalf, max);
} else {
return integer(min, min + distanceHalf);
}
}
default int integer(int min, int mean, int max) {
if (ENFORCING_UNIT_CONSISTENCY) {
assertThat(min).isLessThanOrEqualTo(mean);
assertThat(mean).isLessThanOrEqualTo(max);
}
final var distance = distance(min, max);
if (truthValue()) {
return integer(min, mean);
} else {
return integer(mean, max);
}
}
default boolean truthValue() {
return 1 == integer(0, 1);
}
default boolean truthValue(double chance) {
return truthValue(doubleToFloat(chance));
}
default boolean truthValue(float chance) {
if (ENFORCING_UNIT_CONSISTENCY) {
assertThat(chance)
.withFailMessage("Invalid chance given.")
.isBetween(0f, 1f);
}
final int scaleFactor = 10_000;
final var scaledChance = chance * scaleFactor;
return scaledChance >= integer(0, scaleFactor);
}
default String readableAsciiString(int size) {
final StringBuilder rBase = new StringBuilder();
for (int i = 0; i < size; ++i) {
rBase.append((char) asRandom().nextInt(126));
}
return rBase.toString();
}
@JavaLegacyArtifact
Random asRandom();
default <T> List<T> chooseAtMostMultipleOf(int numberOfThingsToChoose, List<T> args) {
if (args.isEmpty()) {
return list();
}
if (numberOfThingsToChoose >= args.size()) {
return listWithValuesOf(args);
}
final List<T> chosenArgs = list();
range(0, numberOfThingsToChoose)
.forEach(i -> {
final var nextIndexCandidate = this.integer(0, args.size() - 1);
chosenArgs.add(args.remove(nextIndexCandidate));
});
return chosenArgs;
}
default <T> T chooseOneOf(java.util.List<T> arg) {
if (arg.isEmpty()) {
throw new IllegalArgumentException();
}
return arg.get(asRandom().nextInt(arg.size()));
}
default <T> T chooseOneOf(List<T> arg) {
if (arg.isEmpty()) {
throw new IllegalArgumentException();
}
return arg.get(asRandom().nextInt(arg.size()));
}
default <T> T removeOneOf(List<T> arg) {
if (arg.isEmpty()) {
throw new IllegalArgumentException();
}
return arg.remove(asRandom().nextInt(arg.size()));
}
static void assertPlausibility(float chance, int tries, int positives) {
if (ENFORCING_UNIT_CONSISTENCY) {
assertThat(chance).isBetween(0f, 1f);
assertThat(tries).isGreaterThan(-1);
assertThat(positives).isGreaterThan(-1);
}
final var actualChance = abs((float) positives / (float) tries);
final var chanceDiff = actualChance - chance;
final var isPlausible = chanceDiff < 0.1;
assertThat(isPlausible).withFailMessage("actualChance: " + actualChance + "chanceDiff: " + chanceDiff).isTrue();
}
}