js_mt_rand
Version:
A pseudo-random generator that can produce the same numbers as the php's mt_rand do with given seed.
140 lines (129 loc) • 2.89 kB
JavaScript
/**
* Convert a number to uint32
*
* @param {number} x
* @returns {number}
*/
export function toUInt32(x) {
return x >>> 0;
}
/**
* Convert a number to int32
*
* @param {number} x
* @returns {number}
*/
export function toInt32(x) {
return x >> 0;
}
/**
* Mask all but highest bit of u
*
* @param {number} u
* @returns {number}
*/
export function hiBit(u) {
return u & 0x80000000;
}
/**
* Mask all but lowest bit of u
*
* @param {number} u
* @returns {number}
*/
export function loBit(u) {
return u & 0x00000001;
}
/**
* Mask the highest bit of u
*
* @param {number} u
* @returns {number}
*/
export function loBits(u) {
return u & 0x7FFFFFFF;
}
/**
* Move hi bit of u to hi bit of v
*
* @param {number} u
* @param {number} v
* @returns {number}
*/
export function mixBits(u, v) {
return hiBit(u) | loBits(v);
}
/**
* Mersenne Twister
*
* @param m
* @param u
* @param v
* @returns {number}
*/
export function twist(m, u, v) {
return toUInt32(toUInt32(m) ^ (mixBits(u, v) >>> 1) ^
(toUInt32(-toInt32(loBit(v))) & 0x9908b0df));
}
/**
* An incorrect version of Mersenne Twister
* This is for backward compatibility.
*
* @param m
* @param u
* @param v
* @returns {number}
*/
export function twistPHP(m, u, v) {
return toUInt32(toUInt32(m) ^ (mixBits(u, v) >>> 1) ^
(toUInt32(-toInt32(loBit(u))) & 0x9908b0df));
}
/**
* Generator a seed from native javascript random number generator
*
* @returns {number}
*/
export function generateSeed() {
return (Math.random() * Math.pow(2, 32)) >>> 0;
}
/*
* A bit of tricky math here. We want to avoid using a modulus because
* that simply tosses the high-order bits and might skew the distribution
* of random values over the range. Instead we map the range directly.
*
* We need to map the range from 0...M evenly to the range a...b
* Let n = the random number and n' = the mapped random number
*
* Then we have: n' = a + n(b-a)/M
*
* We have a problem here in that only n==M will get mapped to b which
# means the chances of getting b is much much less than getting any of
# the other values in the range. We can fix this by increasing our range
# artificially and using:
#
# n' = a + n(b-a+1)/M
*
# Now we only have a problem if n==M which would cause us to produce a
# number of b+1 which would be bad. So we bump M up by one to make sure
# this will never happen, and the final algorithm looks like this:
#
# n' = a + n(b-a+1)/(M+1)
*
* -RL
*/
export function randRangeBadScaling(n, min, max, tmax) {
return min + ((max - min + 1) * (n / (tmax + 1.0))) >>> 0;
}
/**
* Helper function to output unexpected value to console.
*
* @param {boolean} n
* @param {string} reason
* @returns {*}
*/
export function unexpected(n, reason) {
if (n) {
console.warn('unexpected: ' + reason);
}
return n;
}