rng.bend source
rng.bend on the hub · documented module
# RandomProvider surface — C# Unsga3.Utilities.RandomProvider.# Seed + NextDouble in [0, 1) + Next / NextExcept for tournament.# Portable Numerical Recipes LCG (not .NET System.Random). Algorithm# A/B fronts will not match C# bit-for-bit because of this RNG delta.## Affine API: `next_double` / `next_nat` return the advanced state;# `last_double` / `last_nat` are that draw. Callers use lets, not# pair-destructure.import Basetype State is Data: MkState{ s0: U32 u: U32 z: F32 k: Nat }def new(+s0: U32) -> State: MkState{s0, s0, 0.0, 0n}def from_nat(seed: Nat) -> State: new(U32.from_nat(seed))def seed_of(s: State) -> U32: match s: case MkState{s0, u, z, k}: s0def last_double(s: State) -> F32: match s: case MkState{s0, u, z, k}: zdef last_nat(s: State) -> Nat: match s: case MkState{s0, u, z, k}: kdef step(u: U32) -> U32: U32.add(U32.mul(1664525, u), 1013904223)# Top 24 bits / 2^24 so the F32 mantissa is exact and the value is in [0, 1).def to_unit(u: U32) -> F32: F32.div(U32.to_f32(U32.shrn(u, 8n)), 16777216.0)def next_double(s: State) -> State: match s: case MkState{s0, u, z, k}: +n = step(u) MkState{s0, n, to_unit(n), k}def pred_or_zero(n: Nat) -> Nat: match n: case 0n: 0n case 1n+p: pdef clamp_lt.fin(+i: Nat, +n: Nat, ok: Bool) -> Nat: match ok: case True{}: i case False{}: pred_or_zero(n)def clamp_lt(+i: Nat, +n: Nat) -> Nat: clamp_lt.fin(i, n, Nat.is_lt(i, n))def idx_from(+z: F32, n: Nat) -> Nat: match n: case 0n: 0n case 1n++p: clamp_lt(F32.to_nat(F32.trunc(F32.mul(z, F32.from_nat(1n+p)))), 1n+p)def with_k(s: State, k2: Nat) -> State: match s: case MkState{s0, u, z, k}: MkState{s0, u, z, k2}# Uniform integer in [0, n). n == 0n → 0n.def next_nat(s: State, n: Nat) -> State: +s2 = next_double(s) with_k(s2, idx_from(last_double(s2), n))def bump_except(+v: Nat, +exclude: Nat, below: Bool) -> Nat: match below: case True{}: v case False{}: 1n+v# C# NextExcept: uniform in [0, n) distinct from exclude when n > 1.def next_except.go(s: State, +exclude: Nat, n: Nat) -> State: match n: case 0n: with_k(s, 0n) case 1n: with_k(s, 0n) case 1n+m: +s2 = next_nat(s, m) +v = last_nat(s2) with_k(s2, bump_except(v, exclude, Nat.is_lt(v, exclude)))def next_except(s: State, n: Nat, exclude: Nat) -> State: next_except.go(s, exclude, n)