/*************************************************************************** * 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.generators; import edu.brown.benchmark.tpce.TPCEConstants; import edu.brown.benchmark.tpce.util.EGenRandom; /** * Description: This class encapsulates customer tier distribution * functions and provides functionality to: * - Generate customer tier based on customer ID * - Generate non-uniform customer ID * - Generate customer IDs in a specified partition, and * outside the specified partition a set percentage of * the time. */ public class CustomerSelection { public enum TierId { eCustomerTierOne, eCustomerTierTwo, eCustomerTierThree; } private final EGenRandom rnd; private final boolean isPartition; private final int partPercent; private final long startCustomer; private long myStartCustomer; private final long customerCount; private long myCustomerCount; public CustomerSelection(EGenRandom rnd, long startCustomer, long customerCount, int partPercent, long myStartCustomer, long myCustomerCount) { this.rnd = rnd; this.startCustomer = startCustomer + TPCEConstants.IDENT_SHIFT; this.myStartCustomer = myStartCustomer + TPCEConstants.IDENT_SHIFT; this.customerCount = customerCount; this.myCustomerCount = myCustomerCount; this.partPercent = partPercent; if (startCustomer == myStartCustomer && customerCount == myCustomerCount) { isPartition = false; } else { isPartition = true; } } public CustomerSelection(EGenRandom rnd, long startCustomer, long customerCount){ this.rnd = rnd; this.startCustomer = startCustomer + TPCEConstants.IDENT_SHIFT; this.myStartCustomer = 0 + TPCEConstants.IDENT_SHIFT; this.customerCount = customerCount; this.partPercent = 0; this.isPartition = false; } public void setPartitionRange(long startFromCustomer, long customerCount) { if (isPartition) { this.myStartCustomer = startFromCustomer; this.myCustomerCount = customerCount; } } /** * Generate a customer id with a tier. * All intra-function comments are from EGen. * * @return Generated customer id and the tier */ public Object[] genRandomCustomer() { double cw = rnd.doubleRange(0.0001, 2000); TierId tier; // Uniformly select the higher portion of the customer ID. long cHigh; if (isPartition && rnd.rndPercent(partPercent)) { // Generate a load unit inside the partition. cHigh = (rnd.int64Range(myStartCustomer, myStartCustomer + myCustomerCount - 1) - 1) / 1000; // minus 1 for the upper boundary case } else { // Generate a load unit across the entire range cHigh = (rnd.int64Range(startCustomer, startCustomer + customerCount - 1) - 1) / 1000; // minus 1 for the upper boundary case } // Non-uniformly select the lower portion of the customer ID and the tier int cLow; if (cw <= 200) { // tier one cLow = (int)Math.ceil(Math.sqrt(22500 + 500 * cw) - 151); tier = TierId.eCustomerTierOne; } else if (cw <= 1400) { // tier two cLow = (int)Math.ceil(Math.sqrt(290000 + 1000 * cw) - 501); tier = TierId.eCustomerTierTwo; } else { // tier three cLow = (int)Math.ceil(149 + Math.sqrt(500 * cw - 277500)); tier = TierId.eCustomerTierThree; } Object[] res = new Object[2]; res[0] = cHigh * 1000 + permute(cLow, cHigh) + 1; res[1] = tier; return res; } // lower 3 digits private static long lowDigits(long cid) { return (cid - 1) % 1000; } // higher 3 digits private static long highDigits(long cid) { return (cid - 1) / 1000; } public static long permute(long low, long high) { return (677 * low + 33 * (high + 1)) % 1000; } // Inverse permutation. public static long inversePermute(long low, long high) { // Extra mod to make the result always positive return (((613 * (low - 33 * (high + 1))) % 1000) + 1000) % 1000; } public static TierId getTier(long cid) { long revCid = inversePermute(lowDigits(cid), highDigits(cid)); if (revCid < 200) { return TierId.eCustomerTierOne; } else { if (revCid < 800) { return TierId.eCustomerTierTwo; } else { return TierId.eCustomerTierThree; } } } /* * Return scrambled inverse customer id in range of 0 to 999. */ public static int getInverseCid(long cid) { int cHigh = (int)highDigits(cid); int inverseCid = (int)inversePermute(lowDigits(cid), cHigh); if (inverseCid < 200) { // Tier 1: value 0 to 199 return ((3 * inverseCid + (cHigh + 1)) % 200); } else { if (inverseCid < 800) { // Tier 2: value 200 to 799 return (((59 * inverseCid + 47 * (cHigh + 1)) % 600) + 200); } else { // Tier 3: value 800 to 999 return (((23 * inverseCid + 17 * (cHigh + 1)) % 200) + 800); } } } }