1use crate::time::now_nanos;
7use std::sync::{LazyLock, Mutex};
8use tinyrand::{Rand, Seeded, StdRand};
9
10pub static STD_RAND: LazyLock<Mutex<StdRand>> =
15 LazyLock::new(|| Mutex::new(StdRand::seed(now_nanos())));
16
17#[must_use]
19pub fn next_u8() -> u8 {
20 (next_u16() & 0xFF) as u8
21}
22
23#[must_use]
25pub fn next_u16() -> u16 {
26 STD_RAND.lock().expect("mutex").next_u16()
27}
28
29#[must_use]
31pub fn next_u32() -> u32 {
32 STD_RAND.lock().expect("mutex").next_u32()
33}
34
35#[must_use]
37pub fn next_u64() -> u64 {
38 STD_RAND.lock().expect("mutex").next_u64()
39}
40
41#[must_use]
43pub fn next_u128() -> u128 {
44 STD_RAND.lock().expect("mutex").next_u128()
45}
46
47#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn test_unique_u64s() {
57 use std::collections::HashSet;
58
59 let mut set = HashSet::new();
60 while set.len() < 1000 {
61 let random_value = next_u64();
62 assert!(set.insert(random_value), "value already in set");
63 }
64 }
65
66 #[test]
67 fn test_rng_reseeding() {
68 let mut rng1 = StdRand::seed(now_nanos());
69 let mut rng2 = StdRand::seed(now_nanos() + 1);
70
71 let mut matched = false;
72 for _ in 0..100 {
73 if rng1.next_u64() == rng2.next_u64() {
74 matched = true;
75 break;
76 }
77 }
78 assert!(
79 !matched,
80 "RNGs with different seeds unexpectedly produced the same value"
81 );
82 }
83
84 #[test]
85 fn test_determinism_with_fixed_seed() {
86 let seed = 42;
87 let mut rng1 = StdRand::seed(seed);
88 let mut rng2 = StdRand::seed(seed);
89
90 for _ in 0..100 {
91 assert_eq!(rng1.next_u64(), rng2.next_u64());
92 }
93 }
94
95 #[test]
96 fn test_bit_entropy() {
97 let mut bits = 0u64;
98 for _ in 0..100 {
99 bits |= next_u64();
100 }
101
102 let bit_count = bits.count_ones();
103 assert!(
104 bit_count > 8,
105 "Low entropy: only {bit_count} bits set in 100 samples",
106 );
107 }
108}