/*
* Copyright 2014 The Skfiy Open Association.
*
* 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 org.skfiy.typhon.util;
import java.io.Serializable;
import java.util.Random;
/**
*
* @author Kevin Zou <kevinz@skfiy.org>
*/
public final class FastRandom extends Random implements Serializable {
private static final long serialVersionUID = 6910932436509951204L;
private static final ThreadLocal<FastRandom> THREAD_LOCAL = new ThreadLocal<FastRandom>() {
@Override
protected FastRandom initialValue() {
return new FastRandom();
}
};
//for initialization similar to java.util.Random
// with corrections from http://www.alife.co.uk/nonrandom/proposal/index.html
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
private static final int Q_SIZE = 4096;
private static final long a = 18782;
private static final int r = 0xfffffffe;
private int[] Q;
private int c;
private int idx;
/**
* Returns a thread-local FastRandom value.
*
* @return the thread-local random number generator.
*/
public static FastRandom threadLocal() {
return THREAD_LOCAL.get();
}
/**
* Creates an RNG seeded with a system time.
*
* Creation of two fast randoms sequentially will most probably produce two different generators.
*/
public FastRandom() {
super();
}
/**
* Creates an RNG seeded with the given seed of type {@code long}.
*
* @param seed the seed.
*/
public FastRandom(long seed) {
super(seed);
}
/**
* Creates a copy of this RNG.
*
* These two generators will produce the same random sequence.
*
* @return the copy of this RNG.
*/
public FastRandom makeCopy() {
FastRandom rv = new FastRandom(1);
rv.copyStateFrom(this);
return rv;
}
/**
* Sets this RNG to exactly the same state as of the specified RNG.
*
* The next chain of
*
* @param source the RNG to copy the state from.
*/
public void copyStateFrom(FastRandom source) {
this.c = source.c;
this.idx = source.idx;
System.arraycopy(source.Q, 0, this.Q, 0, Q_SIZE);
}
/**
* Seeds the RNG with the given seed of type {@code long}.
*
* Unlike some implementations of {@link Random}, a {@link FastRandom} either constructed using a seed {@code S} or
* initialized to {@code S} later will produce the same random sequence.
*
* @param seed the seed.
*/
@Override
public void setSeed(long seed) {
super.setSeed(seed);
if (Q == null) {
Q = new int[Q_SIZE];
}
seed = nextSeed(seed);
c = ((int) (seed >>> 16)) % (809430660);
seed = nextSeed(seed);
for (int i = 0; i < Q_SIZE; ++i) {
Q[i] = (int) (seed >>> 16);
seed = nextSeed(seed);
}
this.idx = 0;
}
private long nextSeed(long seed) {
return (seed & mask) * multiplier + addend + (seed >>> 47);
}
@Override
protected int next(int nBits) {
idx = (idx + 1) & (Q_SIZE - 1);
long t = a * Q[idx] + c;
c = (int) (t >>> 32);
int x = (int) t + c;
if (x < c) {
x++;
c++;
}
int rv = Q[idx] = r - x;
return rv >>> (32 - nBits);
}
}