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>12
namespace rnd = cheatah::random;14
TEST(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
}21
}23
TEST(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
}30
}32
TEST(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 range40
}42
TEST(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);48
}50
TEST(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);59
}61
TEST(CheatahRandom, Choice) {62
EXPECT_EQ(rnd::choice(std::vector<int>{99, 99, 99}), 99); // all-equal → always 9963
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);68
}70
TEST(CheatahRandom, EngineIsPerThread) {71
// The engine is thread_local: concurrent draws never race (this test is the TSan-gate72
// regression for the once-shared engine), and each thread's stream is independent — the main73
// thread's seed does not reach a worker, and hammering from two threads does not perturb a74
// 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 only81
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 draw86
const double seen = rnd::random();87
a.join();88
b.join();89
EXPECT_DOUBLE_EQ(seen, expected_first); // untouched by 10k concurrent worker draws90
}