123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
// https://stackoverflow.com/questions/521295#47593316
|
|
|
|
export type State = [number, number, number, number];
|
|
|
|
export interface Randy {
|
|
state: State;
|
|
|
|
int(): number; // whole int32 range
|
|
int(x: number): number; // [0, x)
|
|
int(x: number, y: number): number; // [x, y)
|
|
|
|
float(): number; // [0, 1)
|
|
float(x: number): number; // [0, x)
|
|
float(x: number, y: number): number; // [x, y)
|
|
|
|
choice<A>(array: A[]): A;
|
|
boolean(): boolean;
|
|
}
|
|
|
|
|
|
export function fromHex(s: string): State {
|
|
const a = s.substring(0, 8);
|
|
const b = s.substring(8, 16);
|
|
const c = s.substring(16, 24);
|
|
const d = s.substring(24, 32);
|
|
return [h(a), h(b), h(c), h(d)];
|
|
|
|
function h(x: string) { return parseInt(x, 16); }
|
|
}
|
|
|
|
export function toHex([a, b, c, d]: State): string {
|
|
return `${h(a)}${h(b)}${h(c)}${h(d)}`;
|
|
function h(x: number) { return x.toString(16); }
|
|
}
|
|
|
|
|
|
const UINT_MAX = 4294967296;
|
|
|
|
export class Rand implements Randy {
|
|
#a: number; #b: number; #c: number; #d: number;
|
|
|
|
constructor();
|
|
constructor([a, b, c, d]: State);
|
|
constructor(str: string);
|
|
constructor(st?: State | string) {
|
|
const [a, b, c, d] =
|
|
st === '' ? s4() :
|
|
typeof st === 'string' ? Rand.stateFrom(st) :
|
|
st ?? s4();
|
|
this.#a = a; this.#b = b; this.#c = c; this.#d = d;
|
|
|
|
for (let i = 0; i < 20; ++i) this.#next();
|
|
|
|
function s() { return (Math.random() * 2**32) >>> 0; }
|
|
function s4() { return [s(), s(), s(), s()] as const; }
|
|
}
|
|
|
|
get state(): State { return [this.#a, this.#b, this.#c, this.#d]; }
|
|
|
|
get stateString() { return toHex(this.state); }
|
|
|
|
static stateFrom(str: string): State {
|
|
let h1 = 1779033703, h2 = 3144134277,
|
|
h3 = 1013904242, h4 = 2773480762;
|
|
for (let i = 0, k; i < str.length; i++) {
|
|
k = str.charCodeAt(i);
|
|
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
|
|
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
|
|
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
|
|
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
|
|
}
|
|
h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
|
|
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
|
|
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
|
|
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
|
|
h1 ^= (h2 ^ h3 ^ h4); h2 ^= h1; h3 ^= h1; h4 ^= h1;
|
|
return [h1>>>0, h2>>>0, h3>>>0, h4>>>0];
|
|
}
|
|
|
|
int(): number; // whole int32 range
|
|
int(to: number): number; // [0, x)
|
|
int(from: number, to: number): number; // [x, y)
|
|
int(x?: number, y?: number): number {
|
|
if (x === undefined) return this.#next();
|
|
return Math.floor(y === undefined ? this.float(x) : this.float(x, y));
|
|
}
|
|
|
|
float(): number; // [0, 1)
|
|
float(x: number): number; // [0, x)
|
|
float(x: number, y: number): number; // [x, y)
|
|
float(x?: number, y?: number): number {
|
|
const in01 = this.#next() / UINT_MAX;
|
|
if (x === undefined) return in01;
|
|
const [lo, hi] = y === undefined? [0, x] : [x, y];
|
|
return lo + in01 * (hi - lo);
|
|
}
|
|
|
|
choice<A>(array: A[]): A {
|
|
return array[this.int(array.length)]!;
|
|
}
|
|
|
|
boolean(): boolean { return this.float() > 0.5; }
|
|
|
|
alphaNum(len?: number): string {
|
|
let res = "";
|
|
// [todo] is there a better way to make a string in js
|
|
if (len === undefined || len <= 0) len = 16; // idk
|
|
for (let i = 0; i < len; ++i)
|
|
res += this.choice(Array.from("abcdefghijklmnopqrstuvwxyz0123456789"));
|
|
return res;
|
|
}
|
|
|
|
#next(): number {
|
|
this.#a |= 0; this.#b |= 0; this.#c |= 0; this.#d |= 0;
|
|
const t = (this.#a + this.#b | 0) + this.#d | 0;
|
|
this.#d = this.#d + 1 | 0;
|
|
this.#a = this.#b ^ this.#b >>> 9;
|
|
this.#b = this.#c + (this.#c << 3) | 0;
|
|
this.#c = (this.#c << 21 | this.#c >>> 11);
|
|
this.#c = this.#c + t | 0;
|
|
return (t >>> 0);
|
|
}
|
|
}
|