glyde/rng
Deterministic randomness: every draw is a pure function of a caller-supplied seed threaded through the state. Discord asks for a jittered first heartbeat and a randomised wait before reconnecting.
Never use it for a websocket mask, a handshake nonce or a token. Knowing the seed is knowing every draw.
Types
A generator: seed or at for the real one, fixed for a test.
Opaque because the cycle only holds for a state in [1, modulus - 1]. State 0 is the fixed point, so every reconnect would draw the same jitter, and a negative state draws negative jitter and arms a timer in the past.
pub opaque type Rng
Values
pub fn at(state: Int) -> Rng
A generator on an exact cycle position, folded like seed but not
scrambled, so at(1) really is state 1. A shard wants seed; this is for
a test that names the sequence.
pub fn below(rng: Rng, bound: Int) -> #(Rng, Int)
A value in [0, bound). A non-positive bound gives 0 rather than
crashing: a state machine may not panic.
pub fn between(
rng: Rng,
low low: Int,
high high: Int,
) -> #(Rng, Int)
A value in [low, high). An empty range yields low.
pub fn fixed(value: Int) -> Rng
A generator that never advances, reading its value as billionths of
whatever is asked for: fixed(0) pins the low end of every range,
fixed(500_000_000) the middle, fixed(999_999_999) the high end.
pub fn seed(n: Int) -> Rng
Any n works, folded onto the cycle. Distinct seeds below 2^31 stay
distinct; system_time_ms % 2_000_000_000 is a fine source.
pub fn state(rng: Rng) -> option.Option(Int)
Where a generator sits on the cycle. None for Fixed, which has no
position.