org.codefilarete.tool.bean.Randomizer Maven / Gradle / Ivy
package org.codefilarete.tool.bean;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.codefilarete.tool.collection.Iterables;
/**
* A simple class to randomly generate values of basic types (double, int, Date, objects from a list, ...)
* It delegates its repartition to a {@link IRandomGenerator}. Then every draw is based on {@link IRandomGenerator#drawDouble()} which is less
* optimized than using dedicated methods as {@link Random} does.
*
* @author Guillaume Mary
*/
public class Randomizer {
public static final Randomizer INSTANCE = new Randomizer();
private static final String BASE64CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final String HEXCHARS = "ABCDEFGH0123456789";
private final IRandomGenerator random;
/**
* Create a linearly distributing {@link Randomizer}
*/
public Randomizer() {
this(new LinearRandomGenerator());
}
/**
* Create a Gaussian distributing {@link Randomizer}
*/
public Randomizer(boolean gaussian) {
this(gaussian ? new GaussianRandomGenerator() : new LinearRandomGenerator());
}
/**
* Create a customized distributing {@link Randomizer}
*/
public Randomizer(IRandomGenerator random) {
this.random = random;
}
public double drawDouble() {
return this.random.randomDouble();
}
public double drawDouble(double lowBound, double highBound) {
return (highBound - lowBound) * drawDouble() + lowBound;
}
public long drawLong(long lowBound, long highBound) {
return (long) ((highBound - lowBound) * drawDouble() + lowBound);
}
public int drawInt() {
return drawInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
public int drawInt(int lowBound, int highBound) {
return (int) ((highBound - lowBound) * drawDouble() + lowBound);
}
public boolean drawBoolean() {
return drawDouble() > 0.5;
}
public Date drawDate(Date lowBound, Date highBound) {
return new Date(drawLong(lowBound.getTime(), highBound.getTime()));
}
public String drawString(String hat, int maxLength) {
int hatLength = hat.length();
int startIndex = drawInt(0, hatLength);
int length = drawInt(0, maxLength);
return hat.substring(startIndex, Math.min(startIndex + length, hatLength));
}
public List drawElements(Iterable hat, int count) {
List toReturn = new ArrayList<>(count);
Iterator hatIterator = hat.iterator();
while (toReturn.size() < count && hatIterator.hasNext()) {
if (drawBoolean()) {
toReturn.add(hatIterator.next());
}
}
return toReturn;
}
public List drawElements(Collection hat, int count) {
int hatSize = hat.size();
// Anti overflow
count = Math.min(hatSize, count);
List toReturn = new ArrayList<>(count);
Set drawnIndexes = new HashSet<>();
while (drawnIndexes.size() < count) {
int drawnIndex;
do {
drawnIndex = drawInt(0, hatSize);
} while (drawnIndexes.contains(drawnIndex));
drawnIndexes.add(drawnIndex);
}
toReturn.addAll(getElementsByIndex(hat, new TreeSet<>(drawnIndexes)));
return toReturn;
}
static List getElementsByIndex(Iterable iterable, SortedSet indexes) {
List toReturn;
if (iterable instanceof List) {
toReturn = indexes.stream().map(((List) iterable)::get).collect(Collectors.toList());
} else {
toReturn = new ArrayList<>();
int i = 0;
Iterator iterator = iterable.iterator();
for (int index : indexes) {
E next;
do {
next = iterator.next();
i++;
} while (i <= index && iterator.hasNext());
toReturn.add(next);
}
}
return toReturn;
}
public E drawElement(List hat) {
int hatSize = hat.size();
if (hatSize < 2) {
return Iterables.first(hat);
} else {
int drawnIndex = drawInt(0, hatSize);
return hat.get(drawnIndex);
}
}
public char drawChar(String hat) {
return hat.charAt(drawInt(0, hat.length()));
}
public String randomHexString(int length) {
return randomHexString(length, HEXCHARS);
}
public String randomBase64String(int length) {
return randomHexString(length, BASE64CHARS);
}
private String randomHexString(int length, String hat) {
StringBuilder randomHexString = new StringBuilder(length);
for (int i = 0; i < length; i++) {
randomHexString.append(drawChar(hat));
}
return randomHexString.toString();
}
public interface IRandomGenerator {
double randomDouble();
}
/**
* Generator that will draw double linearly distributed
*
* @see Random#nextDouble()
*/
public static class LinearRandomGenerator implements IRandomGenerator {
private final Random random;
public LinearRandomGenerator() {
// we use SecureRandom instead of Random to avoid some security issue
this(new SecureRandom());
}
public LinearRandomGenerator(Random random) {
this.random = random;
}
@Override
public double randomDouble() {
return random.nextDouble();
}
}
/**
* Generator that will draw double according to the Gaussian law
*
* @see Random#nextGaussian()
*/
public static class GaussianRandomGenerator implements IRandomGenerator {
// we use SecureRandom instead of Random to avoid some security issue
private final Random random = new SecureRandom();
@Override
public double randomDouble() {
return random.nextGaussian();
}
}
}