1use rand::rngs::StdRng;
22use rand::{RngCore, SeedableRng};
23
24pub trait RandomSource: RngCore {
35 fn reseed(&mut self, seed: u64) {
39 let _ = seed;
40 panic!("this random source does not support reseeding");
41 }
42}
43
44impl RandomSource for StdRng {
45 fn reseed(&mut self, seed: u64) {
46 *self = StdRng::seed_from_u64(seed);
47 }
48}
49
50#[derive(Debug, Clone)]
61pub struct SplitMix64 {
62 state: u64,
63}
64
65impl SplitMix64 {
66 #[must_use]
68 pub fn new(seed: u64) -> Self {
69 SplitMix64 { state: seed }
70 }
71
72 pub fn set_seed(&mut self, seed: u64) {
74 self.state = seed;
75 }
76
77 #[inline]
79 fn next(&mut self) -> u64 {
80 self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
81 let mut z = self.state;
82 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
83 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
84 z ^ (z >> 31)
85 }
86}
87
88impl RngCore for SplitMix64 {
89 fn next_u64(&mut self) -> u64 {
90 self.next()
91 }
92
93 fn next_u32(&mut self) -> u32 {
94 (self.next() >> 32) as u32
96 }
97
98 fn fill_bytes(&mut self, dest: &mut [u8]) {
99 let mut chunks = dest.chunks_exact_mut(8);
100 for chunk in &mut chunks {
101 chunk.copy_from_slice(&self.next().to_le_bytes());
102 }
103 let rem = chunks.into_remainder();
104 if !rem.is_empty() {
105 let bytes = self.next().to_le_bytes();
106 rem.copy_from_slice(&bytes[..rem.len()]);
107 }
108 }
109}
110
111impl RandomSource for SplitMix64 {
112 fn reseed(&mut self, seed: u64) {
113 self.state = seed;
114 }
115}
116
117pub mod sample {
131 use rand::RngCore;
132
133 const TWO_POW_NEG_53: f64 = 1.0 / 9_007_199_254_740_992.0;
135
136 pub fn uniform01<R: RngCore + ?Sized>(rng: &mut R) -> f64 {
141 ((rng.next_u64() >> 11) as f64) * TWO_POW_NEG_53
142 }
143
144 pub fn exponential<R: RngCore + ?Sized>(rng: &mut R, mean: f64) -> f64 {
148 let u = uniform01(rng);
149 -mean * (-u).ln_1p()
150 }
151
152 pub fn bernoulli<R: RngCore + ?Sized>(rng: &mut R, p: f64) -> bool {
154 uniform01(rng) < p
155 }
156
157 pub fn normal<R: RngCore + ?Sized>(rng: &mut R, mean: f64, std: f64) -> f64 {
162 let u1 = uniform01(rng);
163 let u2 = uniform01(rng);
164 let r = (-2.0 * (-u1).ln_1p()).sqrt();
165 let z = r * (std::f64::consts::TAU * u2).cos();
166 mean + std * z
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 const KAT_SEED0: [u64; 6] = [
178 0xE220_A839_7B1D_CDAF,
179 0x6E78_9E6A_A1B9_65F4,
180 0x06C4_5D18_8009_454F,
181 0xF88B_B8A8_724C_81EC,
182 0x1B39_896A_51A8_749B,
183 0x53CB_9F0C_747E_A2EA,
184 ];
185 const KAT_SEED42: [u64; 6] = [
186 0xBDD7_3226_2FEB_6E95,
187 0x28EF_E333_B266_F103,
188 0x4752_6757_130F_9F52,
189 0x581C_E1FF_0E4A_E394,
190 0x09BC_585A_2448_23F2,
191 0xDE44_31FA_3C80_DB06,
192 ];
193
194 #[test]
195 fn splitmix64_known_answer_vectors() {
196 for (seed, expected) in [(0u64, KAT_SEED0), (42u64, KAT_SEED42)] {
197 let mut rng = SplitMix64::new(seed);
198 for &want in &expected {
199 assert_eq!(rng.next_u64(), want, "seed {seed}");
200 }
201 }
202 }
203
204 #[test]
205 fn uniform01_and_exponential_known_answer() {
206 let mut rng = SplitMix64::new(0);
207 let u = sample::uniform01(&mut rng);
208 assert_eq!(u, 0.8833108082136426);
209
210 let mut rng = SplitMix64::new(0);
211 let e = sample::exponential(&mut rng, 1.0);
212 assert_eq!(e, 2.148241359348383);
213 }
214
215 #[test]
216 fn uniform01_in_unit_interval() {
217 let mut rng = SplitMix64::new(7);
218 for _ in 0..10_000 {
219 let u = sample::uniform01(&mut rng);
220 assert!((0.0..1.0).contains(&u));
221 }
222 }
223
224 #[test]
225 fn next_u32_is_high_bits_of_next_u64() {
226 let expected = (KAT_SEED0[0] >> 32) as u32;
227 let mut rng = SplitMix64::new(0);
228 assert_eq!(rng.next_u32(), expected);
229 }
230
231 #[test]
232 fn same_seed_same_stream() {
233 let mut a = SplitMix64::new(123);
234 let mut b = SplitMix64::new(123);
235 for _ in 0..1000 {
236 assert_eq!(a.next_u64(), b.next_u64());
237 }
238 }
239
240 #[test]
241 fn reseed_restarts_stream() {
242 let mut rng = SplitMix64::new(0);
243 let first = rng.next_u64();
244 let _ = rng.next_u64();
245 rng.reseed(0);
246 assert_eq!(rng.next_u64(), first);
247 rng.set_seed(0);
248 assert_eq!(rng.next_u64(), first);
249 }
250
251 #[test]
252 fn exponential_mean_is_sane() {
253 let mut rng = SplitMix64::new(99);
254 let n = 200_000;
255 let sum: f64 = (0..n).map(|_| sample::exponential(&mut rng, 5.0)).sum();
256 let mean = sum / f64::from(n);
257 assert!((mean - 5.0).abs() < 0.1, "mean was {mean}");
258 }
259
260 #[test]
261 fn normal_consumes_two_draws_and_is_centered() {
262 let mut a = SplitMix64::new(5);
264 let _ = sample::normal(&mut a, 0.0, 1.0);
265 let after_one = a.clone().next_u64();
266
267 let mut b = SplitMix64::new(5);
268 sample::uniform01(&mut b);
269 sample::uniform01(&mut b);
270 assert_eq!(after_one, b.next_u64());
271
272 let mut rng = SplitMix64::new(1);
273 let n = 200_000;
274 let sum: f64 = (0..n).map(|_| sample::normal(&mut rng, 10.0, 2.0)).sum();
275 let mean = sum / f64::from(n);
276 assert!((mean - 10.0).abs() < 0.05, "mean was {mean}");
277 }
278
279 #[test]
280 fn stdrng_reseed_is_deterministic() {
281 let mut a = StdRng::seed_from_u64(0);
282 a.reseed(77);
283 let mut b = StdRng::seed_from_u64(77);
284 assert_eq!(a.next_u64(), b.next_u64());
285 }
286}