1pub struct DetRng {
12 state: u64,
13}
14
15impl DetRng {
16 pub fn new(seed: u64) -> Self {
18 Self { state: seed }
19 }
20
21 pub fn derive(master_seed: u64, domain: &str) -> Self {
34 let mut hash: u64 = 14695981039346656037; for byte in master_seed.to_le_bytes() {
37 hash ^= byte as u64;
38 hash = hash.wrapping_mul(1099511628211);
39 }
40 for byte in domain.as_bytes() {
42 hash ^= *byte as u64;
43 hash = hash.wrapping_mul(1099511628211);
44 }
45 Self { state: hash }
46 }
47
48 #[inline]
50 pub fn next_u64(&mut self) -> u64 {
51 self.state = self
52 .state
53 .wrapping_mul(6364136223846793005)
54 .wrapping_add(1442695040888963407);
55 self.state
56 }
57
58 #[inline]
60 pub fn next_f64(&mut self) -> f64 {
61 let val = self.next_u64();
62 (val >> 11) as f64 / (1u64 << 53) as f64
63 }
64
65 #[inline]
67 pub fn next_u64_below(&mut self, max: u64) -> u64 {
68 if max == 0 {
69 return 0;
70 }
71 self.next_u64() % max
72 }
73
74 #[inline]
76 pub fn next_u64_range(&mut self, min: u64, max: u64) -> u64 {
77 if min >= max {
78 return min;
79 }
80 let range = max - min + 1;
81 min + self.next_u64_below(range)
82 }
83
84 #[inline]
86 pub fn chance(&mut self, probability: f64) -> bool {
87 self.next_f64() < probability
88 }
89
90 pub fn shuffle<T>(&mut self, slice: &mut [T]) {
92 let len = slice.len();
93 if len < 2 {
94 return;
95 }
96 for i in (1..len).rev() {
97 let j = self.next_u64_below(i as u64 + 1) as usize;
98 slice.swap(i, j);
99 }
100 }
101
102 pub fn state(&self) -> u64 {
104 self.state
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_deterministic_same_seed() {
114 let mut rng1 = DetRng::new(42);
115 let mut rng2 = DetRng::new(42);
116 let seq1: Vec<u64> = (0..100).map(|_| rng1.next_u64()).collect();
117 let seq2: Vec<u64> = (0..100).map(|_| rng2.next_u64()).collect();
118 assert_eq!(seq1, seq2, "Same seed must produce identical sequences");
119 }
120
121 #[test]
122 fn test_different_seeds_differ() {
123 let mut rng1 = DetRng::new(42);
124 let mut rng2 = DetRng::new(99);
125 let seq1: Vec<u64> = (0..10).map(|_| rng1.next_u64()).collect();
126 let seq2: Vec<u64> = (0..10).map(|_| rng2.next_u64()).collect();
127 assert_ne!(seq1, seq2);
128 }
129
130 #[test]
131 fn test_derive_produces_different_streams() {
132 let mut net = DetRng::derive(42, "network");
133 let mut disk = DetRng::derive(42, "storage");
134 let a = net.next_u64();
135 let b = disk.next_u64();
136 assert_ne!(a, b);
137 }
138
139 #[test]
140 fn test_derive_is_deterministic() {
141 let mut r1 = DetRng::derive(42, "network");
142 let mut r2 = DetRng::derive(42, "network");
143 let seq1: Vec<u64> = (0..50).map(|_| r1.next_u64()).collect();
144 let seq2: Vec<u64> = (0..50).map(|_| r2.next_u64()).collect();
145 assert_eq!(seq1, seq2);
146 }
147
148 #[test]
149 fn test_f64_in_range() {
150 let mut rng = DetRng::new(42);
151 for _ in 0..10_000 {
152 let v = rng.next_f64();
153 assert!((0.0..1.0).contains(&v), "f64 out of [0,1): {v}");
154 }
155 }
156
157 #[test]
158 fn test_range_in_bounds() {
159 let mut rng = DetRng::new(42);
160 for _ in 0..10_000 {
161 let v = rng.next_u64_range(10, 50);
162 assert!((10..=50).contains(&v), "Range out of [10,50]: {v}");
163 }
164 }
165
166 #[test]
167 fn test_chance_probability() {
168 let mut rng = DetRng::new(42);
169 let total = 10_000;
170 let hits: usize = (0..total).filter(|_| rng.chance(0.5)).count();
171 let rate = hits as f64 / total as f64;
172 assert!(
173 rate > 0.45 && rate < 0.55,
174 "50% chance should be ~50%, got {:.1}%",
175 rate * 100.0
176 );
177 }
178
179 #[test]
180 fn test_shuffle_deterministic() {
181 let mut rng1 = DetRng::new(42);
182 let mut rng2 = DetRng::new(42);
183 let mut a: Vec<i32> = (0..20).collect();
184 let mut b: Vec<i32> = (0..20).collect();
185 rng1.shuffle(&mut a);
186 rng2.shuffle(&mut b);
187 assert_eq!(a, b, "Same seed shuffle must match");
188 }
189
190 #[test]
191 fn test_shuffle_actually_shuffles() {
192 let mut rng = DetRng::new(42);
193 let original: Vec<i32> = (0..20).collect();
194 let mut shuffled = original.clone();
195 rng.shuffle(&mut shuffled);
196 assert_ne!(original, shuffled, "Shuffle should reorder elements");
197 }
198}