packages/random/src/AleaRandomGenerator.ts
import { RandomGenerator } from './RandomGenerator';
// Alea PRNG, which is not cryptographically strong
// see http://baagoe.org/en/wiki/Better_random_numbers_for_javascript
// for a full discussion and Alea implementation.
function createAlea(seeds: readonly unknown[]) {
function createMash() {
let n = 0xefc8249d;
const mash = (data: unknown) => {
data = String(data);
if (typeof data !== 'string') {
throw new Error('Expected a string');
}
for (let i = 0; i < data.length; i++) {
n += data.charCodeAt(i);
let h = 0.02519603282416938 * n;
n = h >>> 0;
h -= n;
h *= n;
n = h >>> 0;
h -= n;
n += h * 0x100000000; // 2^32
}
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
};
mash.version = 'Mash 0.9';
return mash;
}
let s0 = 0;
let s1 = 0;
let s2 = 0;
let c = 1;
if (seeds.length === 0) {
seeds = [+new Date()];
}
const mash = createMash();
s0 = mash(' ');
s1 = mash(' ');
s2 = mash(' ');
for (let i = 0; i < seeds.length; i++) {
s0 -= mash(seeds[i]);
if (s0 < 0) {
s0 += 1;
}
s1 -= mash(seeds[i]);
if (s1 < 0) {
s1 += 1;
}
s2 -= mash(seeds[i]);
if (s2 < 0) {
s2 += 1;
}
}
const random = () => {
const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
s0 = s1;
s1 = s2;
s2 = t - (c = t | 0);
return s2;
};
random.uint32 = () => random() * 0x100000000; // 2^32
random.fract53 = () => random() + ((random() * 0x200000) | 0) * 1.1102230246251565e-16; // 2^-53
random.version = 'Alea 0.9';
random.args = seeds;
return random;
}
// options:
// - seeds: an array
// whose items will be `toString`ed and used as the seed to the Alea
// algorithm
export class AleaRandomGenerator extends RandomGenerator {
private readonly alea: () => number;
constructor({ seeds = [] }: { seeds?: readonly unknown[] } = {}) {
super();
if (!seeds) {
throw new Error('No seeds were provided for Alea PRNG');
}
this.alea = createAlea(seeds);
}
/**
* @name Random.fraction
* @summary Return a number between 0 and 1, like `Math.random`.
* @locus Anywhere
*/
fraction() {
return this.alea();
}
protected safelyCreateWithSeeds(...seeds: readonly unknown[]): RandomGenerator {
return new AleaRandomGenerator({ seeds });
}
insecure: RandomGenerator = this;
}