1/**2 * Deterministic PRNG: splitmix32 over 32-bit integers.3 * Integer-only (Math.imul / >>>), bit-identical in Node and browsers.4 */5export class Rng {6 private s: number;78 /** Seed parsed from a decimal string (fits 2^53). */9 constructor(seed: string) {10 const n = Number(seed);11 if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) {12 throw new Error(`invalid seed: ${seed}`);13 }14 const lo = n >>> 0; // n mod 2^3215 const hi = Math.floor(n / 4294967296) >>> 0;16 this.s = (lo ^ Math.imul(hi, 0x9e3779b9)) >>> 0;17 // Warm up so tiny seeds diverge immediately.18 this.next();19 this.next();20 }2122 /** Next uint32. */23 next(): number {24 this.s = (this.s + 0x9e3779b9) >>> 0;25 let z = this.s;26 z ^= z >>> 16;27 z = Math.imul(z, 0x21f0aaad);28 z ^= z >>> 15;29 z = Math.imul(z, 0x735a2d97);30 z ^= z >>> 15;31 return z >>> 0;32 }3334 /** Uniform-ish integer in [0, n). n must be >= 1. */35 int(n: number): number {36 return this.next() % n;37 }3839 /** Internal state, for hashing. */40 get state(): number {41 return this.s;42 }43}