clock_rand/fast/
splitmix64.rs

1//! SplitMix64 seeding algorithm
2//!
3//! SplitMix64 is a fast, high-quality seeding algorithm used to initialize
4//! other PRNGs from a single seed value.
5
6use crate::traits::Rng;
7
8/// SplitMix64 PRNG for seeding other generators
9///
10/// This is an ultra-fast PRNG specifically designed for seeding other PRNGs.
11/// It's not suitable for cryptographic use but provides excellent quality
12/// for initialization purposes.
13#[derive(Debug, Clone)]
14pub struct SplitMix64 {
15    state: u64,
16}
17
18impl SplitMix64 {
19    /// Create a new SplitMix64 from a seed
20    #[inline]
21    pub fn new(seed: u64) -> Self {
22        Self { state: seed }
23    }
24
25    /// Generate the next u64 value
26    #[inline(always)]
27    pub fn next_u64(&mut self) -> u64 {
28        self.state = self.state.wrapping_add(0x9e3779b97f4a7c15);
29        let mut z = self.state;
30        z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
31        z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
32        z ^ (z >> 31)
33    }
34
35    /// Generate the next u32 value
36    #[inline]
37    pub fn next_u32(&mut self) -> u32 {
38        (self.next_u64() >> 32) as u32
39    }
40
41    /// Fill a buffer with random bytes
42    #[inline]
43    pub fn fill_bytes(&mut self, dest: &mut [u8]) {
44        let mut chunks = dest.chunks_exact_mut(8);
45        for chunk in chunks.by_ref() {
46            let value = self.next_u64();
47            chunk.copy_from_slice(&value.to_le_bytes());
48        }
49        let remainder = chunks.into_remainder();
50        if !remainder.is_empty() {
51            let value = self.next_u64();
52            let bytes = value.to_le_bytes();
53            remainder.copy_from_slice(&bytes[..remainder.len()]);
54        }
55    }
56
57    /// Generate a seed array for Xoshiro256+
58    pub fn seed_xoshiro256(&mut self) -> [u64; 4] {
59        [
60            self.next_u64(),
61            self.next_u64(),
62            self.next_u64(),
63            self.next_u64(),
64        ]
65    }
66
67    /// Generate a seed array for PCG64
68    pub fn seed_pcg64(&mut self) -> [u64; 2] {
69        [self.next_u64(), self.next_u64()]
70    }
71}
72
73impl Rng for SplitMix64 {
74    #[inline]
75    fn next_u32(&mut self) -> u32 {
76        self.next_u32()
77    }
78
79    #[inline]
80    fn next_u64(&mut self) -> u64 {
81        self.next_u64()
82    }
83
84    #[inline]
85    fn fill_bytes(&mut self, dest: &mut [u8]) {
86        self.fill_bytes(dest)
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_splitmix64_deterministic() {
96        let mut rng1 = SplitMix64::new(12345);
97        let mut rng2 = SplitMix64::new(12345);
98
99        for _ in 0..100 {
100            assert_eq!(rng1.next_u64(), rng2.next_u64());
101        }
102    }
103
104    #[test]
105    fn test_splitmix64_fill_bytes() {
106        let mut rng = SplitMix64::new(42);
107        let mut buf = [0u8; 32];
108        rng.fill_bytes(&mut buf);
109
110        // Should not be all zeros
111        assert!(!buf.iter().all(|&b| b == 0));
112    }
113}