Skip to main content

rustdv_sim/
rng.rs

1//! Tiny deterministic RNG (SplitMix64). Zero-dependency substitute for the
2//! `rand` crate; seeded by the runner from RUSTDV_RANDOM_SEED (port of
3//! cocotb's RANDOM_SEED handling, design-doc D2.4).
4
5#[derive(Clone, Debug)]
6pub struct Rng {
7    state: u64,
8}
9
10impl Rng {
11    pub fn new(seed: u64) -> Rng {
12        Rng { state: seed }
13    }
14
15    pub fn next_u64(&mut self) -> u64 {
16        self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
17        let mut z = self.state;
18        z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
19        z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
20        z ^ (z >> 31)
21    }
22
23    /// Uniform in [0, n). n must be > 0.
24    pub fn below(&mut self, n: u64) -> u64 {
25        self.next_u64() % n
26    }
27
28    pub fn u8(&mut self) -> u8 {
29        (self.next_u64() & 0xFF) as u8
30    }
31
32    pub fn bool(&mut self) -> bool {
33        self.next_u64() & 1 == 1
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn deterministic() {
43        let mut a = Rng::new(42);
44        let mut b = Rng::new(42);
45        for _ in 0..100 {
46            assert_eq!(a.next_u64(), b.next_u64());
47        }
48    }
49
50    #[test]
51    fn below_in_range() {
52        let mut r = Rng::new(7);
53        for _ in 0..1000 {
54            assert!(r.below(5) < 5);
55        }
56    }
57}