metamon/generator/seed
Deterministic pseudo-random seed.
The implementation is the 32-bit “Marsaglia xorshift” PRNG. It uses only shifts and xors — no multiplication — so BEAM (bignum integers) and JavaScript (53-bit safe doubles) produce bit-identical streams. An LCG-style multiplier would overflow JavaScript’s double precision well before the 32-bit mask and cause subtle distribution drift.
Statistical quality is not the goal here — determinism and portability are. Property-based testing is well-served even by modest PRNGs because the failure search is shrinker-driven, not statistical.
Types
Internal state of the PRNG. Always non-negative and ≤ 0xFFFFFFFF.
The xorshift family has 0 as a fixed point, so a masked-to-zero
input is silently replaced with a non-zero default.
original_input records the integer the user originally passed to
seed/1 (if any) so failure reports can annotate the canonical
state with the user-visible value when normalisation kicked in.
pub opaque type Seed
Values
pub fn next_int(s: Seed) -> #(Int, Seed)
Advance the seed once and return the next non-negative integer alongside the advanced seed.
pub fn next_int_in(s: Seed, lo: Int, hi: Int) -> #(Int, Seed)
Return an integer uniformly in the closed interval [lo, hi].
If lo > hi the bounds are swapped. If lo == hi the bound is
returned without consuming randomness (the seed is still advanced
for determinism).
pub fn original_input(s: Seed) -> option.Option(Int)
The integer the user passed to seed/1, if any.
None for seeds derived from the system clock, from next_int /
next_int_in advancement, or from split. Some(n) for seeds
constructed via seed(n). Failure reports compare this against
state/1 to decide whether to annotate “originally seed(n)”.
pub fn random_seed() -> Seed
Construct a seed from the system clock. Useful for ad-hoc local
runs; CI should pin a value via metamon.with_seed(metamon.seed(_)).
pub fn seed(value: Int) -> Seed
Construct a seed from an integer.
The integer is masked to a 32-bit non-negative window so the
stream stays target-portable. A masked-to-zero value is silently
replaced with 0xDEADBEEF because the xorshift family has 0 as
a fixed point — emitting it would degenerate the stream.
Both normalisation steps mean seed(0), seed(0x100000000) (= 2^32),
and any other value whose 32-bit-masked form is 0 collapse to
the same canonical state. Failure reports annotate the canonical
state with the original input when normalisation kicked in (see
original_input/1).
pub fn split(s: Seed) -> #(Seed, Seed)
Split a seed into two statistically independent seeds. The implementation derives the second seed by xor-ing the first with a constant before stepping, which on a 32-bit LCG produces a stream that does not align with the original — sufficient for shrinking independent generator components without correlation artefacts.