Skip to main content

pith_digest/
splitmix64.rs

1//! A deterministic 64-bit PRNG (splitmix64) for tests and mixing.
2
3/// The splitmix64 "golden gamma" increment.
4const GAMMA: u64 = 0x9E37_79B9_7F4A_7C15;
5
6/// A splitmix64 generator (Steele, Lea & Flood 2014).
7///
8/// Carries its 64-bit state, advances by the golden gamma and runs
9/// the xorshift-multiply output function. Two bytes of state for a
10/// passing statistical battery is the reason this and not an LCG is
11/// the shared PRNG: tests that need deterministic "random" data seed
12/// it with a constant, never with the clock.
13///
14/// ```
15/// let mut rng = pith_digest::SplitMix64::new(0);
16/// assert_eq!(rng.next_u64(), 0xe220_a839_7b1d_cdaf);
17/// assert_eq!(rng.next_u64(), 0x6e78_9e6a_a1b9_65f4);
18/// ```
19#[derive(Clone)]
20pub struct SplitMix64 {
21    state: u64,
22}
23
24impl SplitMix64 {
25    /// Creates a generator seeded with `seed`.
26    pub const fn new(seed: u64) -> Self {
27        SplitMix64 { state: seed }
28    }
29
30    /// Produces the next 64-bit output and advances the state.
31    pub fn next_u64(&mut self) -> u64 {
32        self.state = self.state.wrapping_add(GAMMA);
33        let mut z = self.state;
34        z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
35        z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
36        z ^ (z >> 31)
37    }
38
39    /// Produces `n` pseudo-random bytes, filling `out` exactly.
40    ///
41    /// Deterministic in `out.len()` and the seed alone, so a test can
42    /// regenerate the same corpus on any machine.
43    pub fn fill_bytes(&mut self, out: &mut [u8]) {
44        for chunk in out.chunks_mut(8) {
45            let word = self.next_u64().to_le_bytes();
46            chunk.copy_from_slice(&word[..chunk.len()]);
47        }
48    }
49}