1pub mod trace {
26 use std::cell::{Cell, RefCell};
27 use std::panic::Location;
28
29 #[derive(Debug, Clone, PartialEq, Eq)]
30 pub struct Draw {
32 pub kind: String,
34 pub at: &'static Location<'static>,
36 }
37
38 thread_local! {
39 static LOG: RefCell<Option<Vec<Draw>>> = const { RefCell::new(None) };
40 static DEPTH: Cell<u32> = const { Cell::new(0) };
41 }
42
43 pub fn start() {
45 LOG.with(|l| *l.borrow_mut() = Some(Vec::new()));
46 }
47
48 pub fn take() -> Vec<Draw> {
50 LOG.with(|l| l.borrow_mut().take()).unwrap_or_default()
51 }
52
53 pub fn write(path: &std::path::Path, draws: &[Draw]) -> std::io::Result<()> {
55 let text: String = draws
56 .iter()
57 .map(|d| format!("{}\t{}:{}\n", d.kind, d.at.file(), d.at.line()))
58 .collect();
59 std::fs::write(path, text)
60 }
61
62 pub(super) struct Guard;
63
64 impl Drop for Guard {
65 fn drop(&mut self) {
66 DEPTH.with(|d| d.set(d.get() - 1));
67 }
68 }
69
70 #[track_caller]
72 pub(super) fn enter(kind: impl FnOnce() -> String) -> Guard {
73 let at = Location::caller();
74 let outer = DEPTH.with(|d| {
75 let v = d.get();
76 d.set(v + 1);
77 v == 0
78 });
79 if outer {
80 LOG.with(|l| {
81 if let Some(log) = l.borrow_mut().as_mut() {
82 log.push(Draw { kind: kind(), at });
83 }
84 });
85 }
86 Guard
87 }
88}
89
90const N: usize = 624;
91const M: usize = 397;
92
93#[derive(Clone, PartialEq, Eq)]
95pub struct Random {
96 index: u32,
97 state: [u32; N],
98}
99
100impl std::fmt::Debug for Random {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.debug_struct("Random")
103 .field("index", &self.index)
104 .finish_non_exhaustive()
105 }
106}
107
108impl Random {
109 pub fn new(seed: u32) -> Random {
113 let mut state = [0u32; N];
114 state[0] = seed;
115 for i in 1..N {
116 let prev = state[i - 1];
117 state[i] = (prev ^ (prev >> 30))
118 .wrapping_mul(0x6c07_8965)
119 .wrapping_add(i as u32);
120 }
121 Random { index: 0, state }
122 }
123
124 pub fn from_parts(index: u32, state: [u32; N]) -> Random {
126 Random { index, state }
127 }
128
129 pub fn index(&self) -> u32 {
131 self.index
132 }
133
134 pub fn state(&self) -> &[u32; N] {
136 &self.state
137 }
138
139 fn next_group(&mut self) {
141 for i in 0..N {
142 let y = (self.state[i] & 0x8000_0000) | (self.state[(i + 1) % N] & 0x7fff_ffff);
143 let mut v = self.state[(i + M) % N] ^ (y >> 1);
144 if y & 1 != 0 {
145 v ^= 0x9908_b0df;
146 }
147 self.state[i] = v;
148 }
149 }
150
151 #[track_caller]
155 pub fn rand(&mut self) -> u32 {
156 let _t = trace::enter(|| "Rand".into());
157 if self.index == 0 {
158 self.next_group();
159 }
160 let mut y = self.state[self.index as usize];
161 y ^= y >> 11;
162 y ^= (y & 0xff3a_58ad) << 7;
163 y ^= (y & 0xffff_df8c) << 15;
164 self.index = if self.index + 1 == N as u32 {
165 0
166 } else {
167 self.index + 1
168 };
169 y ^ (y >> 18)
170 }
171
172 #[track_caller]
176 pub fn rand_below(&mut self, n: u32) -> u32 {
177 let _t = trace::enter(|| format!("Rand({n})"));
178 if n == 0 {
179 return 0;
180 }
181 self.rand() % n
182 }
183
184 #[track_caller]
188 pub fn rand_range(&mut self, lo: i32, hi: i32) -> i32 {
189 let _t = trace::enter(|| format!("Rand({lo},{hi})"));
190 let r = self.rand();
191 lo.wrapping_add((r % ((hi.wrapping_sub(lo) as u32).wrapping_add(1))) as i32)
192 }
193
194 #[track_caller]
198 pub fn rand_tol(&mut self, t: i32) -> i32 {
199 let _t = trace::enter(|| format!("RandTol({t})"));
200 let r = self.rand();
201 (r % ((t as u32).wrapping_mul(2).wrapping_add(1))) as i32 - t
202 }
203
204 #[track_caller]
208 pub fn rand_float(&mut self) -> f32 {
209 let _t = trace::enter(|| "RandFloat".into());
210 self.rand() as f32 * 2.328_306_4e-10
211 }
212
213 #[track_caller]
217 pub fn rand_float_tol(&mut self) -> f32 {
218 let _t = trace::enter(|| "RandFloatTol".into());
219 1.0 - self.rand() as f32 * 2.328_306_4e-10 * 2.0
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn trace_records_outermost_draws_only() {
229 let mut r = Random::new(1);
230 trace::start();
231 r.rand_float();
232 r.rand_below(7);
233 let log = trace::take();
234 assert_eq!(
235 log.iter().map(|d| d.kind.as_str()).collect::<Vec<_>>(),
236 ["RandFloat", "Rand(7)"]
237 );
238 assert!(log[0].at.file().ends_with("random.rs"));
239 r.rand();
240 assert!(trace::take().is_empty());
241 }
242
243 #[test]
244 fn matches_reference_mt19937() {
245 let mut r = Random::new(5489);
247 assert_eq!(r.rand(), 3_499_211_612);
248 assert_eq!(r.rand(), 581_869_302);
249 assert_eq!(r.rand(), 3_890_346_734);
250 for _ in 3..9999 {
251 r.rand();
252 }
253 assert_eq!(r.rand(), 4_123_659_995); }
255}
256
257#[cfg(test)]
258mod golden {
259 use super::*;
261
262 #[test]
263 fn matches_original_engine() {
264 let mut r = Random::new(290);
265 let v: Vec<u32> = (0..5).map(|_| r.rand()).collect();
266 assert_eq!(
267 v,
268 [1174298092, 3342169725, 1127261350, 2332665128, 3191663389]
269 );
270 assert_eq!(
271 [r.rand_tol(0xffffff), r.rand_tol(0xffffff)],
272 [11962091, -9300278]
273 );
274 assert_eq!(
275 r.rand_float_tol().to_bits(),
276 0.20040464401245117f32.to_bits()
277 );
278 assert_eq!(
279 r.rand_float_tol().to_bits(),
280 0.34909939765930176f32.to_bits()
281 );
282 }
283}