/*
* Copyright 2012, Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.facebook.LinkBench.distributions;
import java.util.Properties;
import java.util.Random;
import com.facebook.LinkBench.Config;
import com.facebook.LinkBench.ConfigUtil;
/**
* Uniform distribution over integers in range [minID, maxID),
* where minID is included in range and maxID excluded
*
*/
public class UniformDistribution implements ProbabilityDistribution {
private long min = 0;
private long max = 1;
private double scale = 1.0;
public void init(long min, long max, Properties props, String keyPrefix) {
if (max <= min) {
throw new IllegalArgumentException("max = " + max + " <= min = " + min +
": probability distribution cannot have zero or negative domain");
}
this.min = min;
this.max = max;
if (props != null && props.containsKey(keyPrefix + Config.PROB_MEAN)) {
scale = (max - min) * ConfigUtil.getDouble(props,
keyPrefix + Config.PROB_MEAN);
} else {
scale = 1.0;
}
}
public void init(long min, long max, double scale) {
this.min = min;
this.max = max;
this.scale = scale;
}
@Override
public double pdf(long id) {
return scaledPDF(id, 1.0);
}
@Override
public double expectedCount(long id) {
return scaledPDF(id, scale);
}
private double scaledPDF(long id, double scale) {
// Calculate this way to avoid losing precision by calculating very
// small pdf number
if (id < min || id >= max) return 0.0;
return scale / (double) (max - min);
}
/**
* Cumulative distribution function for distribution
* @param id
* @return
*/
public double cdf(long id) {
if (id >= max) {
return 1.0;
}
if (id < min) {
return 0.0;
}
long n = max - min;
long rank = id - min + 1;
return rank / (double)n;
}
/**
* Quantile function
*/
public long quantile(double p) {
assert(p >= 0.0 && p <= 1.0);
long n = max - min;
long i = (long)Math.floor(p * n);
if (i == n) return max - 1;
return i + min;
}
// Total number of representable numbers by int
private static final long UINT_RANGE = Integer.MAX_VALUE - (long) Integer.MIN_VALUE;
/** Choose an id X uniformly in the range*/
public long choose(Random rng) {
long n = max - min;
// Java's random number generator has less randomness in lower bits
// so just taking a mod doesn't give a good quality result.
if (n <= Integer.MAX_VALUE) {
return min + (long)rng.nextInt((int)n);
} else if (n < UINT_RANGE) {
return randint2(rng, n);
} else {
return UINT_RANGE * rng.nextInt((int)(n / UINT_RANGE)) +
randint2(rng, n % UINT_RANGE);
}
}
/**
* Produce a random integer in range [0, n]
* n must be in range [0, MAX_INT - MIN_INT]
* @param rng
* @param n
* @return
*/
private long randint2(Random rng, long n) {
assert(n < UINT_RANGE);
double p = Integer.MAX_VALUE / (double)n;
if (rng.nextDouble() < p) {
return rng.nextInt(Integer.MAX_VALUE);
} else {
return Integer.MAX_VALUE +
(long)(rng.nextInt((int)(n - Integer.MAX_VALUE)));
}
}
}