| |
| |
| |
| |
| |
| |
| |
| |
|
|
| const MASK64 = (1n << 64n) - 1n; |
|
|
| |
| export function splitmix64(state: bigint): { state: bigint; out: bigint } { |
| let s = (state + 0x9e3779b97f4a7c15n) & MASK64; |
| let z = s; |
| z = ((z ^ (z >> 30n)) * 0xbf58476d1ce4e5b9n) & MASK64; |
| z = ((z ^ (z >> 27n)) * 0x94d049bb133111ebn) & MASK64; |
| z = z ^ (z >> 31n); |
| return { state: s, out: z & MASK64 }; |
| } |
|
|
| |
| export function mix64(x: bigint): bigint { |
| return splitmix64(x & MASK64).out; |
| } |
|
|
| |
| export function hashCombine(...values: bigint[]): bigint { |
| let h = 0x51_7c_c1_b7_27_22_0a_95n; |
| for (const v of values) { |
| h = mix64((h ^ (v & MASK64)) & MASK64); |
| } |
| return h; |
| } |
|
|
| |
| export function toUnitFloat(x: bigint): number { |
| return Number((x & MASK64) >> 11n) / 2 ** 53; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export class RandomStream { |
| private state: bigint; |
|
|
| constructor(seed: bigint | number) { |
| this.state = mix64(BigInt(seed) & MASK64); |
| } |
|
|
| |
| next(): number { |
| const { state, out } = splitmix64(this.state); |
| this.state = state; |
| return toUnitFloat(out); |
| } |
|
|
| |
| nextU64(): bigint { |
| const { state, out } = splitmix64(this.state); |
| this.state = state; |
| return out; |
| } |
| } |
|
|
| |
| |
| |
| |
| export function seededSample(seed: bigint, n: number, count: number): number[] { |
| const stream = new RandomStream(seed); |
| const arr = Array.from({ length: n }, (_, i) => i); |
| for (let i = n - 1; i > 0; i--) { |
| const j = Math.floor(stream.next() * (i + 1)); |
| [arr[i], arr[j]] = [arr[j], arr[i]]; |
| } |
| return arr.slice(0, count); |
| } |
|
|