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]
21pub fn next_u8() -> u8 {
22 (next_u16() & 0xFF) as u8
23}
24
25#[must_use]
29pub fn next_u16() -> u16 {
30 STD_RAND.lock().expect("mutex").next_u16()
31}
32
33#[must_use]
37pub fn next_u32() -> u32 {
38 STD_RAND.lock().expect("mutex").next_u32()
39}
40
41#[must_use]
45pub fn next_u64() -> u64 {
46 STD_RAND.lock().expect("mutex").next_u64()
47}
48
49#[must_use]
53pub fn next_u128() -> u128 {
54 STD_RAND.lock().expect("mutex").next_u128()
55}
56
57#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_unique_u64s() {
67 use std::collections::HashSet;
68
69 let mut set = HashSet::new();
70 while set.len() < 1000 {
71 let random_value = next_u64();
72 assert!(set.insert(random_value), "value already in set");
73 }
74 }
75
76 #[test]
77 fn test_rng_reseeding() {
78 let mut rng1 = StdRand::seed(now_nanos());
79 let mut rng2 = StdRand::seed(now_nanos() + 1);
80
81 let mut matched = false;
82 for _ in 0..100 {
83 if rng1.next_u64() == rng2.next_u64() {
84 matched = true;
85 break;
86 }
87 }
88 assert!(
89 !matched,
90 "RNGs with different seeds unexpectedly produced the same value"
91 );
92 }
93
94 #[test]
95 fn test_determinism_with_fixed_seed() {
96 let seed = 42;
97 let mut rng1 = StdRand::seed(seed);
98 let mut rng2 = StdRand::seed(seed);
99
100 for _ in 0..100 {
101 assert_eq!(rng1.next_u64(), rng2.next_u64());
102 }
103 }
104
105 #[test]
106 fn test_bit_entropy() {
107 let mut bits = 0u64;
108 for _ in 0..100 {
109 bits |= next_u64();
110 }
111
112 let bit_count = bits.count_ones();
113 assert!(
114 bit_count > 8,
115 "Low entropy: only {bit_count} bits set in 100 samples",
116 );
117 }
118}