/***************************************************************************
* Copyright (C) 2009 by H-Store Project *
* Brown University *
* Massachusetts Institute of Technology *
* Yale University *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************/
package edu.brown.rand;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
public abstract class AbstractRandomGenerator extends Random {
private static final long serialVersionUID = 1L;
private final static Map<String, Constructor<? extends AbstractRandomGenerator>> factoryCache = new HashMap<String, Constructor<? extends AbstractRandomGenerator>>();
/**
* @param seed
*/
public AbstractRandomGenerator() {
super();
}
/**
* @param seed
*/
public AbstractRandomGenerator(Integer seed) {
super(seed);
}
public Set<Integer> getRandomIntSet(int cnt, int max) {
assert (cnt <= max);
Set<Integer> ret = new HashSet<Integer>();
do {
ret.add(this.nextInt(max));
} while (ret.size() < cnt);
return (ret);
}
/**
* @param minimum
* @param maximum
* @returns a int in the range [minimum, maximum]. Note that this is
* inclusive.
*/
public int number(int minimum, int maximum) {
assert minimum <= maximum : String.format("%d <= %d", minimum, maximum);
int range_size = maximum - minimum + 1;
int value = this.nextInt(range_size);
value += minimum;
assert minimum <= value && value <= maximum;
return value;
}
public long number(long minimum, long maximum) {
assert minimum <= maximum : String.format("%d <= %d", minimum, maximum);
long range_size = (maximum - minimum) + 1;
// error checking and 2^x checking removed for simplicity.
long bits, val;
do {
bits = (this.nextLong() << 1) >>> 1;
val = bits % range_size;
} while (bits - val + range_size < 0L);
val += minimum;
assert (val >= minimum);
assert (val <= maximum);
return val;
}
/**
* @param minimum
* @param maximum
* @param excluding
* @returns an int in the range [minimum, maximum], excluding excluding.
*/
public int numberExcluding(int minimum, int maximum, int excluding) {
assert minimum < maximum;
assert minimum <= excluding && excluding <= maximum;
// Generate 1 less number than the range
int num = number(minimum, maximum - 1);
// Adjust the numbers to remove excluding
if (num >= excluding) {
num += 1;
}
assert minimum <= num && num <= maximum && num != excluding;
return num;
}
/**
* @param minimum
* @param maximum
* @param excluding
* @return
*/
public abstract int numberExcluding(int minimum, int maximum, int excluding, String source_table, String target_table);
/**
* Returns a random int in a skewed gaussian distribution of the range Note
* that the range is inclusive A skew factor of 0.0 means that it's a
* uniform distribution The greater the skew factor the higher the
* probability the selected random value will be closer to the mean of the
* range
*
* @param minimum
* the minimum random number
* @param maximum
* the maximum random number
* @param skewFactor
* the factor to skew the stddev of the gaussian distribution
*/
public int numberSkewed(int minimum, int maximum, double skewFactor) {
// Calling number() when the skewFactor is zero will likely be faster
// than using our Gaussian distribution method below
if (skewFactor == 0)
return (this.number(minimum, maximum));
assert minimum <= maximum;
int range_size = maximum - minimum + 1;
int mean = range_size / 2;
double stddev = range_size - ((range_size / 1.1) * skewFactor);
int value = -1;
while (value < 0 || value >= range_size) {
value = (int) Math.round(this.nextGaussian() * stddev) + mean;
}
value += minimum;
assert minimum <= value && value <= maximum;
return value;
}
/**
* Returns a random int based on some affinity distribution for the provided
* base number. For example, if the base is '5', then based on the
* distribution implemented in the class the value '5' might have a higher
* probability of returning the value '6'.
*
* @param minimum
* the minimum random number
* @param maximum
* the maximum random number
* @param base
* the base number to use when selecting a random number
* @param table
* the name of the table this value is being generated for
*/
public abstract int numberAffinity(int minimum, int maximum, int base, String base_table, String target_table);
/**
* Load a profile from the given input path into the Generator
*
* @param input_path
* @throws Exception
*/
public abstract void loadProfile(String input_path) throws Exception;
/**
* Save the generator's current profile to file at output path.
*
* @param output_path
* @throws Exception
*/
public abstract void saveProfile(String output_path) throws Exception;
/**
* @param decimal_places
* @param minimum
* @param maximum
* @return
*/
public double fixedPoint(int decimal_places, double minimum, double maximum) {
assert decimal_places > 0;
assert minimum < maximum : String.format("%f < %f", minimum, maximum);
int multiplier = 1;
for (int i = 0; i < decimal_places; ++i) {
multiplier *= 10;
}
int int_min = (int) (minimum * multiplier + 0.5);
int int_max = (int) (maximum * multiplier + 0.5);
return (double) number(int_min, int_max) / (double) multiplier;
}
/**
* @returns a random alphabetic string with length in range [minimum_length,
* maximum_length].
*/
public String astring(int minimum_length, int maximum_length) {
return randomString(minimum_length, maximum_length, 'a', 26);
}
/**
* @returns a random numeric string with length in range [minimum_length,
* maximum_length].
*/
public String nstring(int minimum_length, int maximum_length) {
return randomString(minimum_length, maximum_length, '0', 10);
}
/**
* @param minimum_length
* @param maximum_length
* @param base
* @param numCharacters
* @return
*/
private String randomString(int minimum_length, int maximum_length, char base, int numCharacters) {
int length = number(minimum_length, maximum_length);
byte baseByte = (byte) base;
byte[] bytes = new byte[length];
for (int i = 0; i < length; ++i) {
bytes[i] = (byte) (baseByte + number(0, numCharacters - 1));
}
return new String(bytes);
}
/**
* @param className
* @param seed
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static AbstractRandomGenerator factory(String className, int seed) throws RuntimeException {
//
// Create constructor handle for RandomGenerator
//
if (!factoryCache.containsKey(className)) {
Constructor<? extends AbstractRandomGenerator> randGenConstructor = null;
try {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<? extends AbstractRandomGenerator> randGenClass = (Class<? extends AbstractRandomGenerator>) loader.loadClass(className);
randGenConstructor = randGenClass.getConstructor(new Class[] { Integer.class });
} catch (Exception ex) {
System.err.println("Failed to retrieve constructor for " + className);
ex.printStackTrace();
throw new RuntimeException(ex);
}
factoryCache.put(className, randGenConstructor);
}
AbstractRandomGenerator rng = null;
try {
rng = factoryCache.get(className).newInstance(new Object[] { Integer.valueOf(seed) });
} catch (Exception ex) {
System.err.println("Failed to instantiate object for " + className);
ex.printStackTrace();
throw new RuntimeException(ex);
}
return (rng);
}
}