I made this for a different project I am working on right now, and I thought I would share it with you guys because why not
const
INT_MAX = Math.pow(2, 31) - 1,
INT_MIN = -Math.pow(2, 31);
const clamp = (x, min, max) => Math.min(Math.max(x, min), max);
const toInt32 = x => Math.trunc(clamp(Number(x), INT_MIN, INT_MAX));
export default class PCG32 {
MULT = 6364136223846793005n;
INC = 105n;
MASK64 = (1n << 64n) - 1n;
MASK32 = 0xFFFFFFFFn;
state = 0n;
#nextUint32() {
const oldstate = this.state;
this.state = (oldstate * this.MULT + (this.INC | 1n)) & this.MASK64;
const xorshifted = Number(((((oldstate >> 18n) ^ oldstate) >> 27n) & this.MASK32));
const rot = Number((oldstate >> 59n) & 31n);
const rx = ((xorshifted >>> rot) | ((xorshifted << ((-rot) & 31)) >>> 0)) >>> 0;
return rx >>> 0;
}
seed(value) {
const seed = BigInt.asUintN(64, BigInt(toInt32(value)));
this.state = 0n;
this.#nextUint32();
this.state = (this.state + seed) & this.MASK64;
this.#nextUint32();
}
random(...args) {
switch (args.length) {
case 0: {
const rl = this.#nextUint32();
const rh = this.#nextUint32();
const combined = (BigInt(rl) | (BigInt(rh) << 32n)) & this.MASK64;
return Number(combined) / Number(1n << 64n);
}
case 1: {
const u = toInt32(args[0]);
if (u < 1) throw new RangeError("interval is empty");
const rnd = BigInt(this.#nextUint32());
const prod = BigInt(u) * rnd;
return Number((prod >> 32n) + 1n);
}
case 2: {
const
l = toInt32(args[0]),
u = toInt32(args[1]);
if (l > u) throw new RangeError("interval is empty");
const ul = BigInt.asUintN(32, BigInt(u - l));
if (ul === this.MASK32) throw new RangeError("interval is too large");
const rnd = BigInt(this.#nextUint32());
const prod = (ul + 1n) * rnd;
const delta = prod >> 32n;
const result = BigInt.asIntN(32, BigInt(l) + delta);
return Number(result);
}
default:
throw new TypeError("wrong number of arguments");
}
}
}
Feedback would be appreciated
Thanks