devela/num/rand/xoroshiro/
u128.rs

1// devela::num::rand::xoroshiro::u128
2//
3//! 128-bit versions of Xoroshiro generators.
4//
5// TOC
6// - Xoroshiro128pp
7//   - public definitions
8//   - trait implementations
9//   - private items and helpers
10
11#[cfg(feature = "alloc")]
12use crate::Box;
13#[cfg(any(feature = "join", feature = "split"))]
14use crate::Cast;
15use crate::{ConstDefault, Own};
16#[cfg(feature = "std")]
17use crate::{Hasher, HasherBuild, RandomState};
18
19/* public definitions */
20
21/// The `Xoroshiro128++`
22/// <abbr title="Pseudo-Random Number Generator">PRNG</abbr>.
23///
24/// It has a 128-bit state and generates 32-bit numbers.
25#[must_use]
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct Xoroshiro128pp([u32; 4]);
28
29/// # Constructors
30impl Xoroshiro128pp {
31    /// Creates a new Xoroshiro128++ PRNG with the given `seed`.
32    ///
33    /// Returns `None` if the seed parts are all zero.
34    #[must_use]
35    pub const fn new(seed: [u32; 4]) -> Option<Self> {
36        if (seed[0] | seed[1] | seed[2] | seed[3]) == 0 {
37            Self::cold_path_result()
38        } else {
39            Some(Self(seed))
40        }
41    }
42
43    /// Creates a new Xoroshiro128++ PRNG with the given seed without any checks.
44    ///
45    /// # Panics
46    /// Panics in debug mode if the seed parts are all `0`.
47    pub const fn new_unchecked(seed: [u32; 4]) -> Self {
48        debug_assert!((seed[0] | seed[1] | seed[2] | seed[3]) != 0, "Seed must be non-zero");
49        Self(seed)
50    }
51
52    /// Creates a new Xoroshiro128++ PRNG, seeded from addresses of stack variables.
53    ///
54    /// This is a very poor source of randomness.
55    #[inline(never)]
56    pub fn from_stack() -> Self {
57        let (a, b, c, d) = (0, 0, 0, 0);
58        let seed: [u32; 4] = [
59            &a as *const _ as u32,
60            &b as *const _ as u32,
61            &c as *const _ as u32,
62            &d as *const _ as u32,
63        ];
64        Self::new_unchecked(seed)
65    }
66
67    /// Creates a new Xoroshiro128++ PRNG, seeded from addresses of heap and stack variables.
68    ///
69    /// This is a very poor source of randomness.
70    #[inline(never)]
71    #[cfg(feature = "alloc")]
72    pub fn from_heap() -> Self {
73        let (a, b, c, d) = (0, Box::new(0), Box::new(0), 0);
74        let seed: [u32; 4] = [
75            &a as *const _ as u32,
76            &b as *const _ as u32,
77            &c as *const _ as u32,
78            &d as *const _ as u32,
79        ];
80        Self::new_unchecked(seed)
81    }
82
83    /// Creates a new Xoroshiro128++ PRNG, seeded from [`RandomState`].
84    #[inline(never)]
85    #[cfg(feature = "std")]
86    pub fn from_randomstate() -> Self {
87        let h = RandomState::new();
88        let (mut hasher1, mut hasher2) = (h.build_hasher(), h.build_hasher());
89        hasher1.write_u32(Self::DEFAULT_SEED[0]);
90        hasher2.write_u32(Self::DEFAULT_SEED[0]);
91        let (hash1, hash2) = (hasher1.finish(), hasher2.finish());
92        let seed = [(hash1 >> 32) as u32, hash1 as u32, (hash2 >> 32) as u32, hash2 as u32];
93        Self::new_unchecked(seed)
94    }
95
96    #[must_use]
97    /// Returns the PRNG's inner state as a raw snapshot.
98    pub const fn inner_state(self) -> [u32; 4] {
99        self.0
100    }
101    /// Restores the PRNG from the given state.
102    pub const fn from_state(state: [u32; 4]) -> Self {
103        Self(state)
104    }
105}
106
107/// # Methods taking `&mut self`
108impl Xoroshiro128pp {
109    /// The jump function for the generator, equivalent to 2^64 `next_u32` calls.
110    pub fn jump(&mut self) {
111        self.jump_with_constant(Self::JUMP);
112    }
113
114    /// The long jump function for the generator, equivalent to 2^96 `next_u32` calls.
115    pub fn long_jump(&mut self) {
116        self.jump_with_constant(Self::LONG_JUMP);
117    }
118
119    /// Generates the next random `u32` value.
120    // Note how the output of the RNG is computed before updating the state.
121    // unlike on Xorshift128, for example.
122    #[must_use]
123    pub fn next_u32(&mut self) -> u32 {
124        let result = self.current_u32();
125        let t = self.0[1] << 9;
126        self.0[2] ^= self.0[0];
127        self.0[3] ^= self.0[1];
128        self.0[1] ^= self.0[2];
129        self.0[0] ^= self.0[3];
130        self.0[2] ^= t;
131        self.0[3] = Self::rotl(self.0[3], 11);
132        result
133    }
134
135    /// Generates the next 2 random `u32` values.
136    #[must_use]
137    pub fn next2(&mut self) -> [u32; 2] {
138        [self.next_u32(), self.next_u32()]
139    }
140    /// Generates the next 4 random `u32` values.
141    #[must_use]
142    pub fn next4(&mut self) -> [u32; 4] {
143        [self.next_u32(), self.next_u32(), self.next_u32(), self.next_u32()]
144    }
145    /// Generates the next random value split into 4 u8 values.
146    #[must_use]
147    pub fn next4_u8(&mut self) -> [u8; 4] {
148        self.next_u32().to_ne_bytes()
149    }
150    /// Generates the next random value split into 2 u16 values.
151    #[must_use]
152    #[cfg(feature = "split")]
153    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "split")))]
154    pub fn next2_u16(&mut self) -> [u16; 2] {
155        Cast(self.next_u32()).into_u16_ne()
156    }
157    /// Returns the next u64, advancing the state 2 times.
158    #[must_use]
159    #[cfg(feature = "join")]
160    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "join")))]
161    pub fn next_u64(&mut self) -> u64 {
162        Cast::<u64>::from_u32_ne(self.next2())
163    }
164    /// Returns the next u128, advancing the state 4 times.
165    #[must_use]
166    #[cfg(feature = "join")]
167    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "join")))]
168    pub fn next_u128(&mut self) -> u128 {
169        Cast::<u128>::from_u32_ne(self.next4())
170    }
171}
172
173/// # Methods taking `self`
174impl Xoroshiro128pp {
175    /// Returns the current random `u32`, without updating the state.
176    #[must_use]
177    pub const fn current_u32(self) -> u32 {
178        Self::rotl(self.0[0].wrapping_add(self.0[3]), 7).wrapping_add(self.0[0])
179    }
180
181    /// Returns a copy of the next new random state.
182    pub const fn copy_peek_next_state(self) -> Self {
183        let mut x = self.0;
184        let t = x[1] << 9;
185        x[2] ^= x[0];
186        x[3] ^= x[1];
187        x[1] ^= x[2];
188        x[0] ^= x[3];
189        x[2] ^= t;
190        x[3] = Self::rotl(x[3], 11);
191        Self(x)
192    }
193
194    /// Returns both the next random state and the `u32` value in a tuple.
195    pub const fn own_next_u32(self) -> Own<Self, u32> {
196        let next_state = self.copy_peek_next_state();
197        let next_value = next_state.current_u32();
198        Own::new(next_state, next_value)
199    }
200
201    /// Returns a copy of the state jumped ahead by 2^64 steps.
202    pub const fn copy_jump(self) -> Self {
203        self.copy_jump_with_constant(Self::JUMP)
204    }
205
206    /// Returns a copy of the state long-jumped ahead by 2^96 steps.
207    pub const fn copy_long_jump(self) -> Self {
208        self.copy_jump_with_constant(Self::LONG_JUMP)
209    }
210}
211
212/// # Extra constructors
213impl Xoroshiro128pp {
214    /// Returns a seeded `Xoroshiro128pp` generator from the given 128-bit seed.
215    ///
216    /// The seeds will be split in little endian order.
217    #[cfg(feature = "split")]
218    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "split")))]
219    pub const fn new1_u128(seed: u128) -> Option<Self> {
220        Self::new(Cast(seed).into_u32_le())
221    }
222
223    /// Returns a seeded `Xoroshiro128pp` generator from the given 2 × 64-bit seeds.
224    ///
225    /// The seeds will be split in little endian order.
226    #[cfg(feature = "split")]
227    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "split")))]
228    pub const fn new2_u64(seeds: [u64; 2]) -> Option<Self> {
229        let [x, y] = Cast(seeds[0]).into_u32_le();
230        let [z, a] = Cast(seeds[1]).into_u32_le();
231        Self::new([x, y, z, a])
232    }
233
234    /// Returns a seeded `Xoroshiro128pp` generator from the given 4 × 32-bit seeds.
235    ///
236    /// This is an alias of [`new`][Self#method.new].
237    pub const fn new4_u32(seeds: [u32; 4]) -> Option<Self> {
238        Self::new(seeds)
239    }
240
241    /// Returns a seeded `Xoroshiro128pp` generator from the given 8 × 16-bit seeds.
242    ///
243    /// The seeds will be joined in little endian order.
244    #[cfg(feature = "join")]
245    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "join")))]
246    pub const fn new8_u16(seeds: [u16; 8]) -> Option<Self> {
247        Self::new([
248            Cast::<u32>::from_u16_le([seeds[0], seeds[1]]),
249            Cast::<u32>::from_u16_le([seeds[2], seeds[3]]),
250            Cast::<u32>::from_u16_le([seeds[4], seeds[5]]),
251            Cast::<u32>::from_u16_le([seeds[6], seeds[7]]),
252        ])
253    }
254
255    /// Returns a seeded `Xoroshiro128pp` generator from the given 16 × 8-bit seeds.
256    ///
257    /// The seeds will be joined in little endian order.
258    pub const fn new16_u8(seeds: [u8; 16]) -> Option<Self> {
259        Self::new([
260            u32::from_le_bytes([seeds[0], seeds[1], seeds[2], seeds[3]]),
261            u32::from_le_bytes([seeds[4], seeds[5], seeds[6], seeds[7]]),
262            u32::from_le_bytes([seeds[8], seeds[9], seeds[10], seeds[11]]),
263            u32::from_le_bytes([seeds[12], seeds[13], seeds[14], seeds[15]]),
264        ])
265    }
266}
267
268/* trait implementations */
269
270/// Creates a new PRNG initialized with the default fixed seed.
271impl Default for Xoroshiro128pp {
272    fn default() -> Self {
273        Self::new_unchecked(Self::DEFAULT_SEED)
274    }
275}
276/// Creates a new PRNG initialized with the default fixed seed.
277impl ConstDefault for Xoroshiro128pp {
278    const DEFAULT: Self = Self::new_unchecked(Self::DEFAULT_SEED);
279}
280
281#[cfg(feature = "dep_rand_core")]
282#[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "dep_rand_core")))]
283mod impl_rand {
284    use super::Xoroshiro128pp;
285    use crate::_dep::rand_core::{RngCore, SeedableRng};
286
287    impl RngCore for Xoroshiro128pp {
288        /// Returns the next random `u32`.
289        fn next_u32(&mut self) -> u32 {
290            self.next_u32()
291        }
292        /// Returns the next random `u64`.
293        fn next_u64(&mut self) -> u64 {
294            ((self.next_u32() as u64) << 32) | (self.next_u32() as u64)
295        }
296        fn fill_bytes(&mut self, dest: &mut [u8]) {
297            let mut i = 0;
298            while i < dest.len() {
299                let random_u32 = self.next_u32();
300                let bytes = random_u32.to_le_bytes();
301                let remaining = dest.len() - i;
302
303                if remaining >= 4 {
304                    dest[i..i + 4].copy_from_slice(&bytes);
305                    i += 4;
306                } else {
307                    dest[i..].copy_from_slice(&bytes[..remaining]);
308                    break;
309                }
310            }
311        }
312    }
313
314    impl SeedableRng for Xoroshiro128pp {
315        type Seed = [u8; 16];
316
317        /// When seeded with zero this implementation uses the default seed
318        /// value as the cold path.
319        fn from_seed(seed: Self::Seed) -> Self {
320            let mut seed_u32s = [0u32; 4];
321            if seed == [0; 16] {
322                Self::cold_path_default()
323            } else {
324                for i in 0..4 {
325                    seed_u32s[i] = u32::from_le_bytes([
326                        seed[i * 4],
327                        seed[i * 4 + 1],
328                        seed[i * 4 + 2],
329                        seed[i * 4 + 3],
330                    ]);
331                }
332                Self::new_unchecked(seed_u32s)
333            }
334        }
335    }
336}
337
338/* private items and helpers */
339
340impl Xoroshiro128pp {
341    const DEFAULT_SEED: [u32; 4] = [0xDEFA_0017; 4];
342    const JUMP: [u32; 4] = [0x8764_000b, 0xf542_d2d3, 0x6fa0_35c3, 0x77f2_db5b];
343    const LONG_JUMP: [u32; 4] = [0xb523_952e, 0x0b6f_099f, 0xccf_5a0ef, 0x1c58_0662];
344
345    #[cold] #[rustfmt::skip]
346    const fn cold_path_result() -> Option<Self> { None }
347    #[cold] #[allow(dead_code)] #[rustfmt::skip]
348    const fn cold_path_default() -> Self { Self::new_unchecked(Self::DEFAULT_SEED) }
349
350    // rotates `x` left by `k` bits.
351    const fn rotl(x: u32, k: i32) -> u32 {
352        (x << k) | (x >> (32 - k))
353    }
354
355    fn jump_with_constant(&mut self, jump: [u32; 4]) {
356        let (mut s0, mut s1, mut s2, mut s3) = (0, 0, 0, 0);
357        for &j in jump.iter() {
358            for b in 0..32 {
359                if (j & (1 << b)) != 0 {
360                    s0 ^= self.0[0];
361                    s1 ^= self.0[1];
362                    s2 ^= self.0[2];
363                    s3 ^= self.0[3];
364                }
365                let _ = self.next_u32();
366            }
367        }
368        self.0 = [s0, s1, s2, s3];
369    }
370
371    const fn copy_jump_with_constant(self, jump: [u32; 4]) -> Self {
372        let (mut s0, mut s1, mut s2, mut s3) = (0, 0, 0, 0);
373        let mut state = self;
374        let mut i = 0;
375        while i < jump.len() {
376            let mut b = 0;
377            while b < 32 {
378                if (jump[i] & (1 << b)) != 0 {
379                    s0 ^= state.0[0];
380                    s1 ^= state.0[1];
381                    s2 ^= state.0[2];
382                    s3 ^= state.0[3];
383                }
384                state = state.copy_peek_next_state();
385                b += 1;
386            }
387            i += 1;
388        }
389        Self([s0, s1, s2, s3])
390    }
391}