cheatah
Source

stdlib/random/random.hpp

1// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).
2// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.
3#pragma once
5/**
6 * @file random.hpp
7 * @brief cheatah `random` — pseudo-random numbers, mirroring the core of
8 * Python's `random` module. Backed by a seedable Mersenne Twister
9 * (`std::mt19937_64`); `gauss` gives normal deviates for Monte Carlo.
10 * `import random` to use it.
11 *
12 * The engine is PER-THREAD (`thread_local`): concurrent draws from `thread.spawn`ed workers
13 * never race, and each thread's stream is independent. A new thread self-seeds from
14 * `std::random_device` on first use; `seed(s)` seeds the CALLING thread's engine only, so a
15 * worker that wants a reproducible stream calls `seed` itself.
16 *
17 * Unit tests: `stdlib/tests/random_test.cpp`; the suite runs under
18 * AddressSanitizer (the `asan` preset) and Valgrind
19 * (`security/run-valgrind.sh`) on every QA-gate run.
20 *
21 * Doc convention (see also the other stdlib headers): each function documents
22 * its runtime complexity with @complexity, its heap allocation with @alloc, and
23 * the @test that covers it.
24 */
25#include <cstddef>
26#include <ranges>
28namespace cheatah::random {
30/**
31 * Seed the calling thread's engine, making its stream reproducible.
32 *
33 * Reseeds THIS thread's Mersenne Twister; all `random`/`uniform`/`randint`/`gauss`/`choice`
34 * calls on the same thread draw from it, so two runs seeded with the same value produce
35 * identical sequences. Until `seed` is called a thread's engine is seeded
36 * non-deterministically from `std::random_device` — a `thread.spawn`ed worker that wants
37 * reproducibility calls `seed` itself (the main thread's seed does not reach it).
38 * @param s the seed.
39 * @complexity O(1) time.
40 * @alloc none.
41 * @concurrency seeds the calling thread's engine only; other threads' streams are unaffected.
42 * @test CheatahRandom.SeedMakesTheStreamReproducible
43 * @test CheatahRandom.EngineIsPerThread
44 * @crtest RandomCompileRun.Seed
45 * @systest StdlibE2E.Random
46 */
47void seed(unsigned long long s);
48/**
49 * Uniform random double.
50 *
51 * Draws from the calling thread's engine with a uniform real distribution over
52 * the half-open unit interval, so 0.0 can occur but 1.0 cannot.
53 * @return a value in [0, 1).
54 * @complexity O(1) time.
55 * @alloc none.
56 * @concurrency thread-safe — draws from the per-thread (`thread_local`) engine, so concurrent draws never race.
57 * @test CheatahRandom.RandomInUnitInterval
58 * @crtest RandomCompileRun.Random
59 * @systest StdlibE2E.Random
60 */
61double random();
62/**
63 * Uniform random double in a range.
64 *
65 * Scales a uniform real distribution to span the given bounds; the caller is
66 * expected to pass @p a ≤ @p b (the bounds are not reordered or validated).
67 * @param a,b the bounds.
68 * @return a value in [@p a, @p b) — the upper bound is excluded, like `random()`.
69 * @complexity O(1) time.
70 * @alloc none.
71 * @concurrency thread-safe — draws from the per-thread (`thread_local`) engine, so concurrent draws never race.
72 * @test CheatahRandom.UniformInRange
73 * @crtest RandomCompileRun.Uniform
74 * @systest StdlibE2E.Random
75 */
76double uniform(double a, double b);
77/**
78 * Uniform random integer.
79 *
80 * Returns each integer in the closed range with equal probability; both @p a and
81 * @p b are attainable, and @p a == @p b always yields that value.
82 * @param a,b inclusive bounds.
83 * @return an integer in [@p a, @p b].
84 * @complexity O(1) time.
85 * @alloc none.
86 * @concurrency thread-safe — draws from the per-thread (`thread_local`) engine, so concurrent draws never race.
87 * @test CheatahRandom.RandintInclusiveRange
88 * @crtest RandomCompileRun.Randint
89 * @systest StdlibE2E.Random
90 */
91long long randint(long long a, long long b);
92/**
93 * Normal (Gaussian) deviate.
94 *
95 * Samples the normal distribution N(@p mu, @p sigma²) from the calling thread's
96 * engine; the result is unbounded and can fall on either side of the mean.
97 * @param mu mean.
98 * @param sigma standard deviation.
99 * @return a normal sample.
100 * @complexity O(1) time.
101 * @alloc none.
102 * @concurrency thread-safe — draws from the per-thread (`thread_local`) engine, so concurrent draws never race.
103 * @test CheatahRandom.GaussIsFiniteAndReproducible
104 * @crtest RandomCompileRun.Gauss
105 * @systest StdlibE2E.Random
106 */
107double gauss(double mu, double sigma);
109/**
110 * Random element of a random-access sequence (list/array).
111 *
112 * Picks a uniformly random index in [0, size) via @ref randint and returns a copy
113 * of that element.
114 * @warning The sequence must be non-empty — an empty @p seq passes an inverted
115 * range to @ref randint, which is undefined.
116 * @param seq the sequence to pick from (must be non-empty).
117 * @return a copy of a uniformly chosen element.
118 * @complexity O(1) time.
119 * @alloc copies the chosen element — none unless the element's copy itself allocates (e.g. `str`).
120 * @concurrency thread-safe — draws its index from the per-thread engine via @ref randint.
121 * @test CheatahRandom.Choice
122 * @crtest RandomCompileRun.Choice
123 * @systest StdlibE2E.Random
124 */
125template <std::ranges::random_access_range R>
126std::ranges::range_value_t<R> choice(const R& seq) {
127 const auto n = static_cast<long long>(std::ranges::size(seq));
128 return seq[static_cast<std::size_t>(randint(0, n - 1))];
131} // namespace cheatah::random