/**
* xethya-random-mtw
*
* Copyright © 2016 Joel A. Villarreal Bertoldi. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE.txt file in the root directory of this source tree.
*/
/**
* @ignore
*/
const N = 624;
/**
* @ignore
*/
const M = 397;
/**
* @ignore
*/
const MATRIX_A = 0x9908b0df;
/**
* @ignore
*/
const UPPER_MASK = 0x80000000;
/**
* @ignore
*/
const LOWER_MASK = 0x7fffffff;
/**
* @ignore
*/
const INIT_BY_ARRAY_SEED = 19650218;
/**
* Implementation of the Mersenne-Twister algorithm for generation of
* pseudo-random numbers.
*
* @see https://gist.github.com/banksean/300494
*/
class MersenneTwister {
/**
* Instantiates the Mersenne-Twister generator.
*
* @param {Number} seed - Seed value, optional.
* @constructor
*/
constructor(seed = null) {
let seedNumber;
if (seed) {
seedNumber = Math.abs(seed);
} else {
if (typeof process !== 'undefined') {
// Try seeding with Node's process.hrtime.
seedNumber = Number(process.hrtime().join(''));
} else if (typeof window.performance !== 'undefined') {
// Try seeding with window.performance.
seedNumber = window.performance.now();
} else {
// Try seeding with a custom algorithm.
seedNumber = Number(new Date().getTime().toString().split('')
.sort(() => 0.5 - Math.random()).join(''));
}
}
this.MT = new Array(N);
this.MTI = N + 1;
this.initializeRandomGenerator(seedNumber);
}
/**
* Determines if the generator works better by being reinstantiated after
* every generated number.
*
* @return {Boolean}
* @static
*/
static recommendsToReinstantiate() {
return false;
}
/**
* Loads the initialization vector required for the algorithm,
* according to a given seed.
*
* @param {Number} seedNumber - A seed can be any non-negative integer value.
* @function
*/
initializeRandomGenerator(seedNumber) {
if (!seedNumber) {
throw new Error('MersenneTwister#initializeRandomGenerator: A seed number is required.');
}
let seed = Math.abs(Math.floor(seedNumber));
this.MT[0] = seed >> 0;
for (this.MTI = 1; this.MTI < N; this.MTI += 1) {
seed = this.MT[this.MTI - 1] ^ (this.MT[this.MTI - 1] >> 30);
this.MT[this.MTI] = ((((seed & 0xffff0000) >> 16) * 1812433253) << 16)
+ ((seed & 0x0000ffff) * 1812433253)
+ this.MTI;
this.MT[this.MTI] = this.MT[this.MTI] >> 0;
}
}
/**
* An alternative way to load the initialization vector for the algorithm.
*
* @param {Array.<Number>} initKeyArray - A list of non-negative integer values.
* @function
*/
initializeByArray(initKeyArray) {
let i = 1;
let j = 0;
const keyLength = initKeyArray.length;
if (keyLength === 0) {
throw new Error('MersenneTwister#initializeByArray: initKeyArray must be '
+ 'an Array of at least one non-negative number.');
}
// Ensure positive, integer values.
const initKey = initKeyArray.map(v => Math.abs(Math.floor(v)));
this.initializeRandomGenerator(INIT_BY_ARRAY_SEED);
let k = N > keyLength ? N : keyLength;
while (k > 0) {
const s = this.MT[i - 1] ^ (this.MT[i - 1] >> 30);
this.MT[i] = (this.MT[i] ^ (((((s & 0xffff0000) >> 16) * 1664525) << 16)
+ ((s & 0x0000ffff) * 1664525)))
+ initKey[j] + j;
this.MT[i] = this.MT[i] >> 0;
i += 1;
j += 1;
if (i >= N) {
this.MT[0] = this.MT[N - 1];
i = 1;
}
if (j >= keyLength) {
j = 0;
}
k -= 1;
}
for (k = N - 1; k > 0; k -= 1) {
const s = this.MT[i - 1] ^ (this.MT[i - 1] >> 30);
this.MT[i] = (this.MT[i] ^ (((((s & 0xffff0000) >> 16) * 1566083941) << 16)
+ ((s & 0x0000ffff) * 1566083941))) - i;
this.MT[i] = this.MT[i] >> 0;
i += 1;
if (i >= N) {
this.MT[0] = this.MT[N - 1];
i = 1;
}
}
this.MT[0] = 0x80000000;
}
/**
* Returns a random non-negative integer value.
*
* @function
* @return {Number}
*/
generateRandomInteger() {
let y;
const mag01 = [0x0, MATRIX_A];
if (this.MTI >= N) {
let kk;
if (this.MTI === N + 1) {
this.initializeRandomGenerator(5489);
}
for (kk = 0; kk < N - M; kk += 1) {
y = (this.MT[kk] & UPPER_MASK) | (this.MT[kk + 1] & LOWER_MASK);
this.MT[kk] = this.MT[kk + M] ^ (y >> 1) ^ mag01[y & 0x1];
}
while (kk < N - 1) {
y = (this.MT[kk] & UPPER_MASK) | (this.MT[kk + 1] & LOWER_MASK);
this.MT[kk] = this.MT[kk + M - N] ^ (y >> 1) ^ mag01[y & 0x1];
kk += 1;
}
y = (this.MT[N - 1] & UPPER_MASK) | (this.MT[0] & LOWER_MASK);
this.MT[N - 1] = this.MT[M - 1] ^ (y >> 1) ^ mag01[y & 0x1];
this.MTI = 0;
}
this.MTI += 1;
y = this.MT[this.MTI];
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680;
y ^= (y << 15) & 0xefc60000;
y ^= (y >> 18);
return y >> 0;
}
/**
* Returns a non-negative random integer value, within
* the range of Int31.
*
* @function
* @return {Number}
*/
generateRandomInteger31() {
return this.generateRandomInteger() >> 1;
}
/**
* Returns a non-negative random real number between 0 and 1.
*
* @function
* @return {Number}
*/
generateRandomReal() {
return this.generateRandomInteger() * (1.0 / 4294967295.0);
}
/**
* Returns a non-negative random number between 0 and 1.
*
* @function
* @return {Number}
*/
generateRandom() {
return this.generateRandomInteger() * (1.0 / 4294967296.0);
}
/**
* Returns a non-negative random real number between 0 and 1.
*
* @function
* @return {Number}
*/
generateRandomReal3() {
return (this.generateRandomInteger() + 0.5) * (1.0 / 4294967296.0);
}
/**
* Returns a non-negative random rumber with a resolution
* of 53 bits.
*
* @function
* @return {Number}
*/
generateRandomReal53BitResolution() {
const a = this.generateRandomInteger() >> 5;
const b = this.generateRandomInteger() >> 6;
return (a * 671084464.0 + b) * (1.0 / 9007199254740992.0);
}
}
export default MersenneTwister;