clock_rand/fast/
pcg64.rs

1//! PCG64 implementation
2//!
3//! PCG (Permuted Congruential Generator) is a family of fast PRNGs
4//! with excellent statistical properties. PCG64 uses 64-bit arithmetic.
5
6use crate::error::Result;
7use crate::fast::splitmix64::SplitMix64;
8use crate::seed::Seed;
9use crate::traits::{DeterministicRng, Rng, SeedableRng};
10
11/// PCG64 PRNG
12///
13/// A fast, high-quality PRNG with 128-bit state. Excellent statistical
14/// properties and small code footprint. Suitable for simulations and
15/// non-cryptographic randomness.
16#[derive(Debug, Clone)]
17pub struct Pcg64 {
18    state: u64,
19    inc: u64,
20}
21
22impl Pcg64 {
23    /// Create a new PCG64 from a seed
24    pub fn new(seed: u64) -> Self {
25        // Default increment (must be odd)
26        let mut splitmix = SplitMix64::new(seed);
27        let state = splitmix.next_u64();
28        let inc = (splitmix.next_u64() << 1) | 1; // Ensure odd
29        Self { state, inc }
30    }
31
32    /// Create from a Seed object
33    pub fn from_seed_obj(seed: &Seed) -> Result<Self> {
34        let seed_bytes = seed.as_ref();
35        if seed_bytes.len() < 8 {
36            // Expand seed if needed
37            let mut expanded = seed_bytes.to_vec();
38            while expanded.len() < 8 {
39                expanded.extend_from_slice(seed_bytes);
40            }
41            let seed_value = u64::from_le_bytes([
42                expanded[0],
43                expanded[1],
44                expanded[2],
45                expanded[3],
46                expanded[4],
47                expanded[5],
48                expanded[6],
49                expanded[7],
50            ]);
51            Ok(Self::new(seed_value))
52        } else {
53            let seed_value = u64::from_le_bytes([
54                seed_bytes[0],
55                seed_bytes[1],
56                seed_bytes[2],
57                seed_bytes[3],
58                seed_bytes[4],
59                seed_bytes[5],
60                seed_bytes[6],
61                seed_bytes[7],
62            ]);
63            Ok(Self::new(seed_value))
64        }
65    }
66
67    /// Create with custom state and increment
68    pub fn from_state(state: u64, inc: u64) -> Self {
69        // Ensure increment is odd
70        let inc = inc | 1;
71        Self { state, inc }
72    }
73
74    /// Generate the next u32 value
75    #[inline(always)]
76    pub fn next_u32(&mut self) -> u32 {
77        (self.next_u64() >> 32) as u32
78    }
79
80    /// Generate the next u64 value
81    #[inline(always)]
82    pub fn next_u64(&mut self) -> u64 {
83        let oldstate = self.state;
84
85        // Advance state: state = state * 6364136223846793005 + inc
86        self.state = oldstate
87            .wrapping_mul(6364136223846793005u64)
88            .wrapping_add(self.inc);
89
90        // Output function: XSH-RR (xorshift high bits, rotate right)
91        let xorshifted = ((oldstate >> 18) ^ oldstate) >> 27;
92        let rot = (oldstate >> 59) as u32;
93        xorshifted.rotate_right(rot)
94    }
95
96    /// Fill a buffer with random bytes
97    #[inline]
98    pub fn fill_bytes(&mut self, dest: &mut [u8]) {
99        let mut chunks = dest.chunks_exact_mut(8);
100        for chunk in chunks.by_ref() {
101            let value = self.next_u64();
102            chunk.copy_from_slice(&value.to_le_bytes());
103        }
104        let remainder = chunks.into_remainder();
105        if !remainder.is_empty() {
106            let value = self.next_u64();
107            let bytes = value.to_le_bytes();
108            remainder.copy_from_slice(&bytes[..remainder.len()]);
109        }
110    }
111
112    /// Save the current state
113    pub fn save_state(&self) -> [u64; 2] {
114        [self.state, self.inc]
115    }
116
117    /// Restore state from saved state
118    pub fn restore_state(&mut self, state: [u64; 2]) {
119        self.state = state[0];
120        self.inc = state[1] | 1; // Ensure odd
121    }
122}
123
124impl Rng for Pcg64 {
125    #[inline]
126    fn next_u32(&mut self) -> u32 {
127        self.next_u32()
128    }
129
130    #[inline]
131    fn next_u64(&mut self) -> u64 {
132        self.next_u64()
133    }
134
135    #[inline]
136    fn fill_bytes(&mut self, dest: &mut [u8]) {
137        self.fill_bytes(dest)
138    }
139}
140
141impl DeterministicRng for Pcg64 {
142    #[inline]
143    fn is_deterministic(&self) -> bool {
144        true
145    }
146}
147
148impl SeedableRng for Pcg64 {
149    type Seed = Seed;
150
151    fn from_seed(seed: Self::Seed) -> Self {
152        Self::from_seed_obj(&seed).expect("Seed should be valid")
153    }
154
155    fn reseed(&mut self, seed: Self::Seed) -> Result<()> {
156        *self = Self::from_seed_obj(&seed)?;
157        Ok(())
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_pcg64_deterministic() {
167        let mut rng1 = Pcg64::new(12345);
168        let mut rng2 = Pcg64::new(12345);
169
170        for _ in 0..100 {
171            assert_eq!(rng1.next_u64(), rng2.next_u64());
172        }
173    }
174
175    #[test]
176    fn test_pcg64_save_restore() {
177        let mut rng = Pcg64::new(42);
178        let _ = rng.next_u64();
179        let state = rng.save_state();
180
181        let mut rng2 = Pcg64::new(0);
182        rng2.restore_state(state);
183
184        assert_eq!(rng.next_u64(), rng2.next_u64());
185    }
186
187    #[test]
188    fn test_pcg64_increment_odd() {
189        let rng = Pcg64::from_state(123, 456);
190        // Increment should be made odd
191        assert_eq!(rng.inc & 1, 1);
192    }
193}