cheatah
Source

stdlib/tests/random_test.cpp

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#include "random.hpp"
5#include <atomic>
6#include <cmath>
7#include <thread>
8#include <vector>
10#include <gtest/gtest.h>
12namespace rnd = cheatah::random;
14TEST(CheatahRandom, RandomInUnitInterval) {
15 rnd::seed(42);
16 for (int i = 0; i < 100; ++i) {
17 const double r = rnd::random();
18 EXPECT_GE(r, 0.0);
19 EXPECT_LT(r, 1.0);
20 }
23TEST(CheatahRandom, UniformInRange) {
24 rnd::seed(1);
25 for (int i = 0; i < 100; ++i) {
26 const double u = rnd::uniform(-2.0, 3.0);
27 EXPECT_GE(u, -2.0);
28 EXPECT_LE(u, 3.0);
29 }
32TEST(CheatahRandom, RandintInclusiveRange) {
33 rnd::seed(2);
34 for (int i = 0; i < 100; ++i) {
35 const long long n = rnd::randint(1, 6);
36 EXPECT_GE(n, 1);
37 EXPECT_LE(n, 6);
38 }
39 EXPECT_EQ(rnd::randint(5, 5), 5); // degenerate single-value range
42TEST(CheatahRandom, GaussIsFiniteAndReproducible) {
43 rnd::seed(3);
44 const double g = rnd::gauss(0.0, 1.0);
45 EXPECT_TRUE(std::isfinite(g));
46 rnd::seed(3);
47 EXPECT_DOUBLE_EQ(rnd::gauss(0.0, 1.0), g);
50TEST(CheatahRandom, SeedMakesTheStreamReproducible) {
51 rnd::seed(7);
52 const double a = rnd::random();
53 const long long b = rnd::randint(1, 1000000);
54 const double c = rnd::uniform(-5.0, 5.0);
55 rnd::seed(7);
56 EXPECT_DOUBLE_EQ(rnd::random(), a);
57 EXPECT_EQ(rnd::randint(1, 1000000), b);
58 EXPECT_DOUBLE_EQ(rnd::uniform(-5.0, 5.0), c);
61TEST(CheatahRandom, Choice) {
62 EXPECT_EQ(rnd::choice(std::vector<int>{99, 99, 99}), 99); // all-equal → always 99
63 rnd::seed(4);
64 const std::vector<int> xs{1, 2, 3, 4, 5};
65 const int picked = rnd::choice(xs);
66 EXPECT_GE(picked, 1);
67 EXPECT_LE(picked, 5);
70TEST(CheatahRandom, EngineIsPerThread) {
71 // The engine is thread_local: concurrent draws never race (this test is the TSan-gate
72 // regression for the once-shared engine), and each thread's stream is independent — the main
73 // thread's seed does not reach a worker, and hammering from two threads does not perturb a
74 // reseeded main-thread stream.
75 rnd::seed(11);
76 const double expected_first = rnd::random();
77 std::atomic<bool> go{false};
78 auto hammer = [&go] {
79 while (!go) {}
80 rnd::seed(11); // seeds THIS thread only
81 for (int i = 0; i < 5000; ++i) (void)rnd::random();
82 };
83 std::thread a(hammer), b(hammer);
84 go = true;
85 rnd::seed(11); // reseed the main thread WHILE the workers draw
86 const double seen = rnd::random();
87 a.join();
88 b.join();
89 EXPECT_DOUBLE_EQ(seen, expected_first); // untouched by 10k concurrent worker draws