/*************************************************************************** * Copyright (C) 2012 by H-Store Project * * Brown University * * Massachusetts Institute of Technology * * Yale University * * * * Alex Kalinin (akalinin@cs.brown.edu) * * http://www.cs.brown.edu/~akalinin/ * * * * 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.benchmark.tpce.util; import java.math.BigDecimal; import java.math.BigInteger; /** * This class emulates the original EGen Random class. * Note that the original class uses unsigned 64-bit * arithmetics, which is non-existent in Java. * * However, since Java uses the two's complement representation * it should work for all operations, except comparisons, which * should be treated differently. * * Note, that the generator always output signed 64-bit numbers, * so this is definitely okay to represent them as long in Java. * * @author akalinin * */ public class EGenRandom { /* * These are different seed bases for pseudo-random EGen generator. */ // Default seed used for all tables. public static final long RNG_SEED_TABLE_DEFAULT = 37039940; // This value is added to the AD_ID when seeding the RNG for // generating a threshold into the TownDivisionZipCode list. public static final long RNG_SEED_BASE_TOWN_DIV_ZIP = 26778071; // This is the base seed used when generating C_TIER. public static final long RNG_SEED_BASE_C_TIER = 16225173; // Base seeds used for generating C_AREA_1, C_AREA_2, C_AREA_3 public static final long RNG_SEED_BASE_C_AREA_1 = 97905013; public static final long RNG_SEED_BASE_C_AREA_2 = 68856487; public static final long RNG_SEED_BASE_C_AREA_3 = 67142295; // Base seed used when generating names. public static final long RNG_SEED_BASE_FIRST_NAME = 95066470; public static final long RNG_SEED_BASE_MIDDLE_INITIAL = 71434514; public static final long RNG_SEED_BASE_LAST_NAME = 35846049; // Base seed used when generating gender. public static final long RNG_SEED_BASE_GENDER = 9568922; // Base seed used when generating tax ID public static final long RNG_SEED_BASE_TAX_ID = 8731255; // Base seed used when generating the number of accounts for a customer //public static final long RNG_SEED_BASE_NUMBER_OF_ACCOUNTS = 37486207; // Base seed used when generating the number of permissions on an account public static final long RNG_SEED_BASE_NUMBER_OF_ACCOUNT_PERMISSIONS = 27794203; // Base seeds used when generating CIDs for additional account permissions public static final long RNG_SEED_BASE_CID_FOR_PERMISSION1 = 76103629; public static final long RNG_SEED_BASE_CID_FOR_PERMISSION2 = 103275149; // Base seed used when generating acount tax status public static final long RNG_SEED_BASE_ACCOUNT_TAX_STATUS = 34376701; // Base seed for determining account broker id public static final long RNG_SEED_BASE_BROKER_ID = 75607774; // Base seed used when generating tax rate row public static final long RNG_SEED_BASE_TAX_RATE_ROW = 92740731; // Base seed used when generating the number of holdings for an account public static final long RNG_SEED_BASE_NUMBER_OF_SECURITIES = 23361736; // Base seed used when generating the starting security ID for the // set of securities associated with a particular account. public static final long RNG_SEED_BASE_STARTING_SECURITY_ID = 12020070; // Base seed used when generating a company's SP Rate public static final long RNG_SEED_BASE_SP_RATE = 56593330; // Base seed for initial trade generation class public static final long RNG_SEED_TRADE_GEN = 32900134; // Base seed for the MEESecurity class public static final long RNG_SEED_BASE_MEE_SECURITY = 75791232; // Base seed for non-uniform customer selection public static final long RNG_SEED_CUSTOMER_SELECTION = 9270899; // Base seed for MEE Ticker Tape public static final long RNG_SEED_BASE_MEE_TICKER_TAPE = 42065035; // Base seed for MEE Trading Floor public static final long RNG_SEED_BASE_MEE_TRADING_FLOOR = 25730774; // Base seed for TxnMixGenerator public static final long RNG_SEED_BASE_TXN_MIX_GENERATOR = 87944308; // Base seed for TxnInputGenerator public static final long RNG_SEED_BASE_TXN_INPUT_GENERATOR = 80534927; // For alpha-numeric strings generation private static final String UPPER_CASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static final String NUMERALS = "0123456789"; // for generating doubles private static final double RECIPROCAL_2_POWER_64 = 5.421010862427522E-20; /* * We use a linear congruential generator. * So here are the parameters. */ private static final long A_MULT = 6364136223846793005L; private static final long C_INC = 1; // the seed long seed; public EGenRandom() { this(System.currentTimeMillis()); } public EGenRandom(long seed) { //this.rnd = newEGenRandom(seed); this.seed = seed; } public long getSeed() { return seed; } public void setSeed(long seed) { this.seed = seed; } // generates next 64-bit number private long int64Rand() { seed = seed * A_MULT + C_INC; return seed; } private long rndNthElement(long baseSeed, long count) { //return rndNthElement(rnd, baseSeed, count); // nothing to do if(count == 0) { return seed; } /* * Recursively compute X(n) = A * X(n-1) + C * * explicitly: * X(n) = A^n * X(0) + { A^(n-1) + A^(n-2) + ... A + 1 } * C * * we write this as: * X(n) = aPow(n) * X(0) + dSum(n) * C * * we use the following relations: * aPow(n) = A^(n % 2) * aPow(n / 2) * aPow(n / 2) * dSum(n) = (n % 2) * aPow(n / 2) * aPow(n / 2) + (aPow(n / 2) + 1) * dSum(n / 2) */ // first get the highest non-zero bit int nBit; for (nBit = 0; (count >>> nBit) != 1; nBit++); long aPow = A_MULT; long dSum = 1; // go 1 bit at the time while (--nBit >= 0) { dSum *= (aPow + 1); aPow = aPow * aPow; if (((count >>> nBit) % 2) == 1) { // odd value dSum += aPow; aPow *= A_MULT; } } return baseSeed * aPow + dSum * C_INC; } /** * Returns a positive double value from the long value. * * Must return a positive double! * * However, v might be a negative number. We translate it to a unsigned string representation * and then to double via BigDecimal. * * NOTE: It might be the case that some simpler conversion is enough, but all signed-only Java arithmetics gives * me a headache and this, at least, works as it prescribed and the same as with the original EGen. * * @param b The value to convert * * @return The resulting double value * */ private double longToDouble(long v) { if (v >= 0) { return (double)v; } else { return new BigDecimal(new BigInteger(Long.toBinaryString(v), 2)).doubleValue(); } } /** * Returns a positive double value in the LCG sequence. * * @param seed The seed * @param count The number in the sequence to return * * @return Double value from the sequence * */ private double rndNthDouble(long seed, long count) { double rnd = longToDouble(rndNthElement(seed, count)); return rnd * RECIPROCAL_2_POWER_64; } /** * Returns a random value in the range [0 .. 0.99999999999999999994578989137572] * * @return The generated value * */ public double rndDouble() { double rnd = longToDouble(int64Rand()); return rnd * RECIPROCAL_2_POWER_64; } public void setSeedNth(long seed, long count) { setSeed(rndNthElement(seed, count)); } public int intRange(int min, int max) { if (min == max) { return min; } max++; // overflow? if (max <= min) { return max; } return min + (int)(rndDouble() * (double)(max - min)); } public long int64Range(long min, long max) { if (min == max) { return min; } max++; // overflow? if (max <= min) { return max; } return min + (long)(rndDouble() * (double)(max - min)); } public int intRangeExclude(int low, int high, int exclude) { int tmp; tmp = intRange(low, high-1); if (tmp >= exclude) tmp += 1; return tmp; } public long int64RangeExclude(long low, long high, long exclude) { long tmp; tmp = int64Range(low, high-1); if (tmp >= exclude) tmp += 1; return tmp; } public int rndNthIntRange(long seed, long count, int min, int max) { if (min == max) { return min; } max++; if (max <= min) { return max; } return min + (int)(rndNthDouble(seed, count) * (double)(max - min)); } public long rndNthInt64Range(long seed, long count, long min, long max) { if (min == max) { return min; } max++; if (max <= min) { return max; } return min + (long)(rndNthDouble(seed, count) * (double)(max - min)); } public double doubleRange(double min, double max) { return min + rndDouble() * (max - min); } /** * @param incr Precision */ public double doubleIncrRange(double min, double max, double incr) { long width = (long)((max - min) / incr); // need [0..width], so no +1 return min + ((double)int64Range(0, width) * incr); } /** * Returns a non-uniform random 64-bit integer in range of [P .. Q]. * * NURnd is used to create a skewed data access pattern. The function is * similar to NURand in TPC-C. (The two functions are identical when C=0 * and s=0.) * * The parameter A must be of the form 2^k - 1, so that Rnd[0..A] will * produce a k-bit field with all bits having 50/50 probability of being 0 * or 1. * * With a k-bit A value, the weights range from 3^k down to 1 with the * number of equal probability values given by C(k,i) = k! /(i!(k-i)!) for * 0 <= i <= k. So a bigger A value from a larger k has much more skew. * * Left shifting of Rnd[0..A] by "s" bits gets a larger interval without * getting huge amounts of skew. For example, when applied to elapsed time * in milliseconds, s=10 effectively ignores the milliseconds, while s=16 * effectively ignores seconds and milliseconds, giving a granularity of * just over 1 minute (65.536 seconds). A smaller A value can then give * the desired amount of skew at effectively one-minute resolution. */ public long rndNU(long p, long q, int a, int s) { return (((int64Range(p, q) | (int64Range(0, a) << s)) % (q - p + 1)) + p); } /** * Returns random alphanumeric string obeying a specific format. * * For the format: n - given character must be numeric * a - given character must be alphabetical * * All other symbols are just copied as is. * * @param format A format string as described above * @return A string corresponding to the format * */ public String rndAlphaNumFormatted(String format) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < format.length(); i++) { char c = format.charAt(i); switch (c) { case 'a': sb.append(UPPER_CASE_LETTERS.charAt(intRange(0, UPPER_CASE_LETTERS.length() - 1))); break; case 'n': sb.append(NUMERALS.charAt(intRange(0, NUMERALS.length() - 1))); break; default: sb.append(c); break; } } return sb.toString(); } public boolean rndPercent(int percent) { return intRange(1, 100) <= percent; } public int rndPercentage() { return intRange(1, 100); } }