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.

pub fn state(s: Seed) -> Int

The raw integer state. Used by the regression-file format to serialise a seed into a reproduction key.

Search Document