burkey.co /docs/libflint/rng
index rng
~/docs/libflint/rng.md
libflint

Rng

libflint·Utilities

A reproducible stream of uint64_t values. The caller owns the LfRng struct, so two simulations can keep two generators. Seeding is a pure function of the seed: it reads no clock and no device. There is no global generator, and the module does not allocate.

The generator is xoshiro256** 1.0 (Blackman and Vigna), seeded with SplitMix64. jump and long_jump are not part of this version. There is no lf_rng_f64. Concurrent use is not supported.

Usage#

Seed a stack LfRng, then draw a full word or a uniform value in [0, n).

#include <stdio.h>
#include "lfrng.h"

int main(void) {
    LfRng rng;
    uint64_t value;
    if (lf_rng_seed(&rng, 1) != 0) {
        return 1;
    }
    if (lf_rng_next_u64(&rng, &value) != 0) {
        return 1;
    }
    if (lf_rng_next_range(&rng, 6, &value) != 0) {
        return 1;
    }
    printf("%llu\n", (unsigned long long)value);
    return 0;
}

lf_rng_seed, lf_rng_next_u64, and lf_rng_next_range return 0 on success and -1 on failure. On failure the handle is unchanged. When out is non-NULL, *out is also unchanged. A bitwise copy of LfRng copies the position in the stream. s[0] through s[3] are the expanded xoshiro256** state, not the original uint64_t seed.

Structs#

LfRng#

typedef struct {
    uint64_t s[4];
} LfRng;

The four words are public. Write them through lf_rng_seed, or copy a whole struct to snapshot the stream. The all-zero state is not a usable stream.

Functions#

lf_rng_seed#

Runs SplitMix64 four times, starting from seed, and stores the results in s[0], s[1], s[2], and s[3]. Returns 0 on success. Returns -1 if rng is NULL or if the expanded state is all zeros. On failure the handle is unchanged.

int lf_rng_seed(LfRng *rng, uint64_t seed);

lf_rng_next_u64#

Advances the xoshiro256** state and stores the next uint64_t in *out. Returns 0 on success. Returns -1 if rng or out is NULL, or if the state is all zeros. The all-zero state is rejected before the update, so a failed call leaves the state and *out unchanged.

int lf_rng_next_u64(LfRng *rng, uint64_t *out);

lf_rng_next_range#

Stores a uniform value in [0, n) in *out. Returns 0 on success. Returns -1 if rng or out is NULL, if n is 0, or if the state is all zeros. Those checks happen before the first draw, so a failed call does not advance the state and leaves *out unchanged.

The value is drawn by rejection so it is uniform for every n. The rejected zone is 2^64 % n, computed as (-n) % n in uint64_t arithmetic. That zone is zero when n is a power of two, including n == 1, and the call then consumes exactly one lf_rng_next_u64. This is not a bare next % n for arbitrary n.

int lf_rng_next_range(LfRng *rng, uint64_t n, uint64_t *out);