clock_rand/fast/
xoshiro256.rs

1//! Xoshiro256+ implementation
2//!
3//! Xoshiro256+ is a fast, high-quality PRNG suitable for simulations
4//! and non-cryptographic randomness. It's the successor to xoroshiro128+.
5
6use crate::error::Result;
7use crate::fast::splitmix64::SplitMix64;
8use crate::seed::Seed;
9use crate::traits::{DeterministicRng, Rng, SeedableRng};
10
11/// Xoshiro256+ PRNG
12///
13/// A fast, high-quality PRNG with 256-bit state. Suitable for simulations,
14/// games, and other non-cryptographic uses. Not suitable for security-critical
15/// applications.
16#[derive(Debug, Clone)]
17pub struct Xoshiro256Plus {
18    state: [u64; 4],
19}
20
21impl Xoshiro256Plus {
22    /// Create a new Xoshiro256+ from a seed
23    pub fn new(seed: u64) -> Self {
24        let mut splitmix = SplitMix64::new(seed);
25        Self {
26            state: splitmix.seed_xoshiro256(),
27        }
28    }
29
30    /// Create from a Seed object
31    pub fn from_seed_obj(seed: &Seed) -> Result<Self> {
32        let seed_bytes = seed.as_ref();
33        if seed_bytes.len() < 8 {
34            // Expand seed if needed
35            let mut expanded = seed_bytes.to_vec();
36            while expanded.len() < 8 {
37                expanded.extend_from_slice(seed_bytes);
38            }
39            let seed_value = u64::from_le_bytes([
40                expanded[0],
41                expanded[1],
42                expanded[2],
43                expanded[3],
44                expanded[4],
45                expanded[5],
46                expanded[6],
47                expanded[7],
48            ]);
49            Ok(Self::new(seed_value))
50        } else {
51            let seed_value = u64::from_le_bytes([
52                seed_bytes[0],
53                seed_bytes[1],
54                seed_bytes[2],
55                seed_bytes[3],
56                seed_bytes[4],
57                seed_bytes[5],
58                seed_bytes[6],
59                seed_bytes[7],
60            ]);
61            Ok(Self::new(seed_value))
62        }
63    }
64
65    /// Generate the next u64 value
66    #[inline(always)]
67    pub fn next_u64(&mut self) -> u64 {
68        let result = self.state[0].wrapping_add(self.state[3]);
69        let t = self.state[1] << 17;
70
71        self.state[2] ^= self.state[0];
72        self.state[3] ^= self.state[1];
73        self.state[1] ^= self.state[2];
74        self.state[0] ^= self.state[3];
75
76        self.state[2] ^= t;
77        self.state[3] = self.state[3].rotate_left(45);
78
79        result
80    }
81
82    /// Generate the next u32 value
83    #[inline]
84    pub fn next_u32(&mut self) -> u32 {
85        (self.next_u64() >> 32) as u32
86    }
87
88    /// Fill a buffer with random bytes
89    #[inline]
90    pub fn fill_bytes(&mut self, dest: &mut [u8]) {
91        let mut chunks = dest.chunks_exact_mut(8);
92        for chunk in chunks.by_ref() {
93            let value = self.next_u64();
94            chunk.copy_from_slice(&value.to_le_bytes());
95        }
96        let remainder = chunks.into_remainder();
97        if !remainder.is_empty() {
98            let value = self.next_u64();
99            let bytes = value.to_le_bytes();
100            remainder.copy_from_slice(&bytes[..remainder.len()]);
101        }
102    }
103
104    /// Jump function for generating non-overlapping sequences
105    #[inline]
106    pub fn jump(&mut self) {
107        const JUMP: [u64; 4] = [
108            0x180ec6d33cfd0aba,
109            0xd5a61266f0c9392c,
110            0xa9582618e03fc9aa,
111            0x39abdc4529b1661c,
112        ];
113
114        let mut s0 = 0;
115        let mut s1 = 0;
116        let mut s2 = 0;
117        let mut s3 = 0;
118
119        for j in &JUMP {
120            for b in 0..64 {
121                if (j & (1u64 << b)) != 0 {
122                    s0 ^= self.state[0];
123                    s1 ^= self.state[1];
124                    s2 ^= self.state[2];
125                    s3 ^= self.state[3];
126                }
127                self.next_u64();
128            }
129        }
130
131        self.state[0] = s0;
132        self.state[1] = s1;
133        self.state[2] = s2;
134        self.state[3] = s3;
135    }
136
137    /// Long jump function for generating even more distant sequences
138    #[inline]
139    pub fn long_jump(&mut self) {
140        const LONG_JUMP: [u64; 4] = [
141            0x76e15d3efefdcbbf,
142            0xc5004e441c522fb3,
143            0x77710069854ee241,
144            0x39109bb02acbe635,
145        ];
146
147        let mut s0 = 0;
148        let mut s1 = 0;
149        let mut s2 = 0;
150        let mut s3 = 0;
151
152        for j in &LONG_JUMP {
153            for b in 0..64 {
154                if (j & (1u64 << b)) != 0 {
155                    s0 ^= self.state[0];
156                    s1 ^= self.state[1];
157                    s2 ^= self.state[2];
158                    s3 ^= self.state[3];
159                }
160                self.next_u64();
161            }
162        }
163
164        self.state[0] = s0;
165        self.state[1] = s1;
166        self.state[2] = s2;
167        self.state[3] = s3;
168    }
169
170    /// Save the current state
171    pub fn save_state(&self) -> [u64; 4] {
172        self.state
173    }
174
175    /// Restore state from saved state
176    pub fn restore_state(&mut self, state: [u64; 4]) {
177        self.state = state;
178    }
179}
180
181impl Rng for Xoshiro256Plus {
182    #[inline]
183    fn next_u32(&mut self) -> u32 {
184        self.next_u32()
185    }
186
187    #[inline]
188    fn next_u64(&mut self) -> u64 {
189        self.next_u64()
190    }
191
192    #[inline]
193    fn fill_bytes(&mut self, dest: &mut [u8]) {
194        self.fill_bytes(dest)
195    }
196}
197
198impl DeterministicRng for Xoshiro256Plus {
199    #[inline]
200    fn is_deterministic(&self) -> bool {
201        true
202    }
203}
204
205impl SeedableRng for Xoshiro256Plus {
206    type Seed = Seed;
207
208    fn from_seed(seed: Self::Seed) -> Self {
209        Self::from_seed_obj(&seed).expect("Seed should be valid")
210    }
211
212    fn reseed(&mut self, seed: Self::Seed) -> Result<()> {
213        *self = Self::from_seed_obj(&seed)?;
214        Ok(())
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221
222    #[test]
223    fn test_xoshiro256_deterministic() {
224        let mut rng1 = Xoshiro256Plus::new(12345);
225        let mut rng2 = Xoshiro256Plus::new(12345);
226
227        for _ in 0..100 {
228            assert_eq!(rng1.next_u64(), rng2.next_u64());
229        }
230    }
231
232    #[test]
233    fn test_xoshiro256_jump() {
234        let mut rng1 = Xoshiro256Plus::new(12345);
235        let mut rng2 = Xoshiro256Plus::new(12345);
236
237        // Generate some values
238        for _ in 0..10 {
239            rng1.next_u64();
240        }
241
242        // Jump should produce different sequence
243        rng2.jump();
244        assert_ne!(rng1.next_u64(), rng2.next_u64());
245    }
246
247    #[test]
248    fn test_xoshiro256_save_restore() {
249        let mut rng = Xoshiro256Plus::new(42);
250        let _ = rng.next_u64();
251        let state = rng.save_state();
252
253        let mut rng2 = Xoshiro256Plus::new(0);
254        rng2.restore_state(state);
255
256        assert_eq!(rng.next_u64(), rng2.next_u64());
257    }
258}