moonrand

    Reproducible pseudo-random generators, unbiased sampling and the common distributions for MoonBit.

    random
    prng
    chacha
    xoshiro
    distributions
    moonbit
    Download zip
    Version
    0.1.1
    License
    Apache-2.0
    Last updated
    8 hours ago
    Downloads
    663

    #moonrand

    Reproducible pseudo-random numbers: one default generator that needs no configuration, sixty-three more behind it when the algorithm matters, and the sampling and distribution functions Python's random names.

    import { "moonbitstack/moonrand", "moonbitstack/moonrand/auto" }

    fn main {
    let rng = @auto.rng() catch { _ => @moonrand.seeded(b"a fixed seed") }
    let roll = @moonrand.randint(rng, 1L, 6L)
    let noise = @moonrand.gauss(rng, mu=0.0, sigma=1.0)
    println("\{roll} \{noise}")
    }

    @auto.rng() asks the operating system for 32 bytes and hands them to the default generator. On a host without an entropy source it raises Unavailable instead of guessing; @moonrand.seeded takes any seed you give it, and the same seed always draws the same numbers.

    #The default

    StdRngChaCha12, a 256-bit stream cipher used as a generator
    seeded(bytes)a generator over the seed, reproducible
    @auto.rng()a generator over 32 bytes of platform entropy

    A stream cipher is the default because its output is indistinguishable from uniform under a secret key, it carries the Secure marker, and seeking a million numbers ahead costs one addition to its counter.

    #Generators

    Every subpackage holds one family of algorithms, each type implementing the same Rng contract, so anything that draws from Rng works with all of them.

    PackageTypes
    moonrand/chachaChaCha8, ChaCha12, ChaCha20
    moonrand/xoshiroXoshiro256ss, Xoshiro256pp, Xoshiro256p, Xoshiro512ss, Xoshiro512pp, Xoshiro512p, Xoroshiro128p, Xoroshiro128pp, Xoroshiro128ss
    moonrand/pcgPcg32, Pcg64, Pcg64Dxsm
    moonrand/mtMT19937, MT19937_64, Sfmt19937
    moonrand/splitmixSplitMix64
    moonrand/sfcSfc64
    moonrand/jsfJsf64
    moonrand/xorshiftXorshift32, Xorshift64, Xorshift128, Xorshift128Plus, Xorwow
    moonrand/wellWell512, Well1024
    moonrand/lcgLcg64, Lcg48, MinStd, Qcg64, Ccg64, Icg32
    moonrand/chaosLogistic, Tent, Sine, Chebyshev
    moonrand/lfsrLfsr64, Gfsr, Taus88, Lfsr113, Lfsr258
    moonrand/fibAdditiveCongruential, LaggedFibonacci, AddWithCarry, SubtractWithBorrow, Ranlux24, Ranlux48
    moonrand/mrgMrg32k3a
    moonrand/lxmL64X128, L64X256
    moonrand/philoxPhilox4x32, Philox4x64
    moonrand/threefryThreefry4x64, Threefry4x32, Threefry2x64
    moonrand/squaresSquares
    moonrand/classicMiddleSquare, MiddleProduct, MiddleSquareWeyl, CollatzWeyl, Bbs

    Each type takes a seed of the size it announces, expands it through SplitMix64, and offers the reference constructor for a state you already have:

    import { "moonbitstack/moonrand/xoshiro" }

    fn main {
    let rng = @xoshiro.Xoshiro256ss::new(b"any number of bytes")
    let from_state = @xoshiro.Xoshiro256ss::from_state([1UL, 2UL, 3UL, 4UL])
    rng.jump() // 2^128 draws ahead
    from_state.long_jump() // 2^192 ahead
    }

    Generators that can jump say so: jump and long_jump on the xoshiro and LXM families, advance on the counter-based ones (ChaCha, PCG, Philox, Threefry, Squares, SplitMix64, MRG32k3a).

    #Sampling

    Over any Rng, with Python's names:

    FunctionDraw
    random(rng)a real in [0, 1) with exactly 53 bits
    uniform(rng, low, high)a real in [low, high)
    randint(rng, lo, hi)an integer in [lo, hi], closed, unbiased
    randrange(rng, stop, start~, step?)Python's randrange
    getrandbits(rng, k)the low k bits of a word
    choice(rng, items)one element, or None for an empty view
    choices(rng, items, k~, weights?)k elements, in proportion when weighted
    sample(rng, items, k)k distinct elements
    shuffle(rng, items)the array in place

    randint and randrange reject out-of-range draws rather than taking a remainder, so no value is more likely than another, whatever the range.

    #Distributions

    FunctionDensity
    gauss(rng, mu~, sigma~)normal, by Box-Muller
    normalvariate(rng, mu~, sigma~)normal, by ratio of uniforms
    expovariate(rng, lambda)exponential with rate lambda
    lognormvariate(rng, mu~, sigma~)log-normal
    gammavariate(rng, alpha, beta)gamma, Marsaglia-Tsang
    betavariate(rng, alpha, beta)beta
    poisson(rng, mu)Poisson, multiplication or transformed rejection
    binomialvariate(rng, n, p)the count of successes in n trials
    triangular(rng, low~, high~, mode?)triangular
    vonmisesvariate(rng, mu, kappa)von Mises on the circle
    paretovariate(rng, alpha)Pareto
    weibullvariate(rng, alpha, beta)Weibull

    gauss and normalvariate are two algorithms, not two spellings: they draw from the same generator differently, and a run that swaps one for the other does not reproduce.

    #Writing your own generator

    The contract is three methods, one of which has a body:

    import { "moonbitstack/moonrand/contract", "moonbitstack/moonrand" }

    struct Counter {
    priv n : UInt64
    }

    pub impl @contract.Rng for Counter with fn next_u64(self) -> UInt64 {
    self.n = self.n + 1UL
    self.n * 6364136223846793005UL
    }

    fn main {
    let rng : Counter = { n: 0UL }
    let x = @moonrand.random(rng)
    println(x)
    }

    next_u32, fill and everything above them follow. Add impl @contract.Seeded for Counter to rebuild it from a seed, or impl @contract.Secure for Counter when its output is fit for cryptography — Philox and PCG pass statistical tests and are not, so they do not carry it.

    #Licence

    Apache-2.0.

    StdRng

    using @moonbitstack/moonrand/chacha { type ChaCha12 as StdRng }

    The default generator: ChaCha12. Change this one line to change the whole library's default algorithm.

    betavariate

    fn[R :
    Rng
    ] betavariate(r : R, alpha : Double, beta : Double) -> Double

    A beta draw with shape parameters alpha and beta: two gamma draws, taken as a fraction of their sum.

    binomialvariate

    fn[R :
    Rng
    ] binomialvariate(r : R, n : Int64, p : Double) -> Int64

    The number of successes in n trials with success probability p, by inverting the binomial CDF term by term: exact for any n, and linear in the number of successes drawn.

    choice

    fn[R :
    Rng
    , T] choice(r : R, items : ArrayView[T]) -> T?

    One element, or None for an empty view.

    choices

    fn[R :
    Rng
    , T] choices(r : R, items : ArrayView[T], k? : Int, weights? : ArrayView[Double]) -> Array[T]

    k elements with replacement; with weights they are drawn proportionally.

    expovariate

    fn[R :
    Rng
    ] expovariate(r : R, lambd : Double) -> Double

    An exponential draw with rate lambd (mean 1/lambd).

    gammavariate

    fn[R :
    Rng
    ] gammavariate(r : R, alpha : Double, beta : Double) -> Double

    A gamma draw with shape alpha and scale beta, by Marsaglia and Tsang's transform-reject method with the squeeze from their paper (and the Cheng variant for alpha below one).

    gauss

    fn[R :
    Rng
    ] gauss(r : R, mu? : Double, sigma? : Double) -> Double

    A normal draw, scaled to mu and sigma (Box-Muller).

    getrandbits

    fn[R :
    Rng
    ] getrandbits(r : R, k : Int) -> UInt64

    k low bits of the next word; k over 64 returns the whole word.

    lognormvariate

    fn[R :
    Rng
    ] lognormvariate(r : R, mu? : Double, sigma? : Double) -> Double

    A lognormal draw: exp(gauss(mu, sigma)).

    normalvariate

    fn[R :
    Rng
    ] normalvariate(r : R, mu? : Double, sigma? : Double) -> Double

    A normal draw by the ratio-of-uniforms method of Kinderman and Monahan, the one Python's normalvariate uses — a different algorithm from gauss, and a different stream for the same seed.

    paretovariate

    fn[R :
    Rng
    ] paretovariate(r : R, alpha : Double) -> Double

    A Pareto draw with shape alpha: one minus a uniform, raised to -1/alpha.

    poisson

    fn[R :
    Rng
    ] poisson(r : R, mu : Double) -> Int64

    A Poisson draw with mean mu: multiplication for small means, and the transformed-rejection method with a squeeze for large ones, so that a mean of a million costs the same as a mean of a hundred.

    randint

    fn[R :
    Rng
    ] randint(r : R, lo : Int64, hi : Int64) -> Int64

    A uniform integer in the closed interval [lo, hi], unbiased.

    random

    fn[R :
    Rng
    ] random(r : R) -> Double

    A uniform Double in [0, 1), with 53 bits of resolution, as Python's random.random draws it.

    randrange

    fn[R :
    Rng
    ] randrange(r : R, stop : Int64, start? : Int64, step? : Int64) -> Int64

    A uniform integer from range(start, stop, step), as Python's randrange. stop is required; start defaults to 0 and step to 1.

    sample

    fn[R :
    Rng
    , T] sample(r : R, items : ArrayView[T], k : Int) -> Array[T]

    k distinct elements, by a Fisher-Yates partial shuffle.

    seeded

    fn seeded(seed : BytesView) ->
    ChaCha12

    A StdRng seeded from seed, reproducible for the same seed.

    shuffle

    fn[R :
    Rng
    , T] shuffle(r : R, a : Array[T]) -> Unit

    Shuffle a in place, Fisher-Yates.

    triangular

    fn[R :
    Rng
    ] triangular(r : R, low? : Double, high? : Double, mode? : Double) -> Double

    A triangular draw between low and high with the peak at mode, which defaults to the midpoint. The inverse of the triangular CDF, folded so that one uniform does the work.

    uniform

    fn[R :
    Rng
    ] uniform(r : R, a : Double, b : Double) -> Double

    A uniform Double in [a, b).

    vonmisesvariate

    fn[R :
    Rng
    ] vonmisesvariate(r : R, mu : Double, kappa : Double) -> Double

    A von Mises draw with mean angle mu and concentration kappa, by the Best-Fisher rejection scheme Python's vonmisesvariate uses. Angles come back in [0, 2 pi).

    weibullvariate

    fn[R :
    Rng
    ] weibullvariate(r : R, alpha : Double, beta : Double) -> Double

    A Weibull draw with scale alpha and shape beta, as Python's weibullvariate names them.

    Source Files

    Powered by MoonBit

    Site sourceReport issueModulesSkillsBuild queueStatistics

    © 2026 mooncakes.io