// math/random — SplitMix64 PRNG. // // Mirrors Hare's math::random surface. The state is a single u64; // Hare types it as `random = u64`. Callers thread a pointer through // next/u32n/u64n so each call advances the state in place. // Deterministic — same seed reproduces the same sequence. package random; export type random = u64; // init — initialize a generator with `seed`. Same seed reproduces the // same sequence on every run. Mirrors Hare's random::init. export fn init(seed: u64) random = { return seed: random; }; // next — return a pseudo-random 64-bit value and advance the state. // SplitMix64, per Hare's random::next. export fn next(r: *random) u64 = { let s: u64 = (*r): u64 + 0x9E3779B97F4A7C15u64; *r = s: random; let a: u64 = s; a = (a ^ (a >> 30u64)) * 0xBF58476D1CE4E5B9u64; a = (a ^ (a >> 27u64)) * 0x94D049BB133111EBu64; return a ^ (a >> 31u64); }; // u32n — pseudo-random u32 in [0, n). n must be > 0. Uses Lemire's // fast unbiased mapping (mulhi-then-leftover-reject). Mirrors Hare's // random::u32n. export fn u32n(r: *random, n: u32) u32 = { assert(n != 0u32); // ref/hare/math/random/random.ha:26 let x: u32 = next(r): u32; let prod: u64 = (x: u64) * (n: u64); let leftover: u32 = prod: u32; if (leftover < n) { // thresh = -n mod n (two's-complement on u32). let neg: u32 = 0u32 - n; let thresh: u32 = neg % n; for (leftover < thresh) { x = next(r): u32; prod = (x: u64) * (n: u64); leftover = prod: u32; }; }; return (prod >> 32u64): u32; }; // u64n — pseudo-random u64 in [0, n). n must be > 0. Power-of-2 fast // path; otherwise rejection-sample to avoid modulo bias. Mirrors // Hare's random::u64n. export fn u64n(r: *random, n: u64) u64 = { assert(n != 0u64); // ref/hare/math/random/random.ha:42 if ((n & (n - 1u64)) == 0u64) { return next(r) & (n - 1u64); }; // max = U64_MAX - (U64_MAX+1) % n = -1 - (-n % n) let neg: u64 = (0u64 - n); let max: u64 = 0xFFFFFFFFFFFFFFFFu64 - (neg % n); let out: u64 = next(r); for (out > max) { out = next(r); }; return out % n; };