1use serde::{Deserialize, Serialize};
11use std::fmt;
12use std::str::FromStr;
13
14const GAMMA: u64 = 0x9E37_79B9_7F4A_7C15;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
22pub struct Seed(u64);
23
24impl Seed {
25 pub fn new(value: u64) -> Self {
27 Self(value)
28 }
29
30 pub fn get(self) -> u64 {
32 self.0
33 }
34}
35
36impl fmt::Display for Seed {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(f, "{}", self.0)
39 }
40}
41
42impl FromStr for Seed {
43 type Err = std::num::ParseIntError;
44
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 s.parse::<u64>().map(Self)
47 }
48}
49
50#[derive(Debug, Clone)]
52pub struct SimRng {
53 state: u64,
54}
55
56impl SimRng {
57 pub fn from_seed(seed: Seed) -> Self {
59 Self { state: seed.get() }
60 }
61
62 pub fn next_u64(&mut self) -> u64 {
64 self.state = self.state.wrapping_add(GAMMA);
65 let mut z = self.state;
66 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
67 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
68 z ^ (z >> 31)
69 }
70
71 pub fn below(&mut self, n: u64) -> u64 {
74 assert!(n > 0, "SimRng::below requires n > 0");
75 ((self.next_u64() as u128 * n as u128) >> 64) as u64
76 }
77
78 pub fn range(&mut self, min: u64, max: u64) -> u64 {
80 assert!(min < max, "SimRng::range requires min < max");
81 min + self.below(max - min)
82 }
83
84 pub fn chance(&mut self, per_mille: u32) -> bool {
86 assert!(per_mille <= 1000, "per-mille probability out of range");
87 self.below(1000) < u64::from(per_mille)
88 }
89
90 pub fn fork(&mut self) -> SimRng {
92 SimRng::from_seed(Seed(self.next_u64()))
93 }
94
95 pub fn fork_label(&self, label: &str) -> SimRng {
98 let mut hash = 0xcbf2_9ce4_8422_2325u64;
99 for byte in label.bytes() {
100 hash ^= u64::from(byte);
101 hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
102 }
103 let mut mixer = SimRng {
104 state: self.state ^ hash,
105 };
106 SimRng {
107 state: mixer.next_u64(),
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn same_seed_same_sequence() {
118 let mut a = SimRng::from_seed(Seed::new(42));
119 let mut b = SimRng::from_seed(Seed::new(42));
120 for _ in 0..100 {
121 assert_eq!(a.next_u64(), b.next_u64());
122 }
123 }
124
125 #[test]
126 fn different_seeds_diverge() {
127 let mut a = SimRng::from_seed(Seed::new(1));
128 let mut b = SimRng::from_seed(Seed::new(2));
129 assert_ne!(a.next_u64(), b.next_u64());
130 }
131
132 #[test]
133 fn fork_derives_child_from_one_parent_draw() {
134 let mut reference = SimRng::from_seed(Seed::new(7));
137 let draws: Vec<u64> = (0..10).map(|_| reference.next_u64()).collect();
138
139 let mut parent = SimRng::from_seed(Seed::new(7));
140 let mut observed = vec![parent.next_u64()];
141 let mut child = parent.fork();
142 observed.extend((0..8).map(|_| parent.next_u64()));
143
144 let mut expected = vec![draws[0]];
145 expected.extend_from_slice(&draws[2..]);
146 assert_eq!(observed, expected);
147
148 let mut seeded_child = SimRng::from_seed(Seed::new(draws[1]));
149 assert_eq!(child.next_u64(), seeded_child.next_u64());
150 }
151
152 #[test]
153 fn fork_label_is_stable_and_distinct() {
154 let rng = SimRng::from_seed(Seed::new(9));
155 assert_eq!(
156 rng.fork_label("net").next_u64(),
157 rng.fork_label("net").next_u64()
158 );
159 assert_ne!(
160 rng.fork_label("net").next_u64(),
161 rng.fork_label("disk").next_u64()
162 );
163 }
164
165 #[test]
166 fn ranges_and_chances_respect_bounds() {
167 let mut rng = SimRng::from_seed(Seed::new(3));
168 for _ in 0..1_000 {
169 assert!((5..10).contains(&rng.range(5, 10)));
170 assert!(rng.below(4) < 4);
171 }
172 assert!(rng.chance(1000));
173 assert!(!rng.chance(0));
174 }
175
176 #[test]
177 fn seed_serializes_for_repro_artifacts() {
178 let seed = Seed::new(123_456);
179 let json = serde_json::to_string(&seed).unwrap();
180 assert_eq!("123456", json);
181 let back: Seed = serde_json::from_str(&json).unwrap();
182 assert_eq!(seed, back);
183 assert_eq!("123456".parse::<Seed>().unwrap(), seed);
184 }
185}