devela/num/rand/xorshift/
u128.rs

1// devela::num::rand::xorshift::u128
2//
3//! 128-bit version of XorShift.
4//
5
6#[cfg(any(feature = "join", feature = "split"))]
7use crate::Cast;
8use crate::{ConstDefault, Own};
9
10/// The `XorShift128` <abbr title="Pseudo-Random Number Generator">PRNG</abbr>.
11///
12/// It has a 128-bit state and generates 64-bit numbers.
13///
14/// See also [`XorShift128p`][crate::XorShift128p].
15#[must_use]
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub struct XorShift128([u32; 4]);
18
19/// Creates a new PRNG initialized with the default fixed seed.
20impl Default for XorShift128 {
21    fn default() -> Self {
22        Self::DEFAULT
23    }
24}
25/// Creates a new PRNG initialized with the default fixed seed.
26impl ConstDefault for XorShift128 {
27    const DEFAULT: Self = Self::new_unchecked(Self::DEFAULT_SEED);
28}
29
30// private associated items
31impl XorShift128 {
32    const DEFAULT_SEED: [u32; 4] = [0xDEFA_0017; 4];
33
34    #[cold] #[allow(dead_code)] #[rustfmt::skip]
35    const fn cold_path_default() -> Self { Self::new_unchecked(Self::DEFAULT_SEED) }
36}
37
38impl XorShift128 {
39    /// Returns a seeded `XorShift128` generator from the given 4 × 32-bit seeds.
40    ///
41    /// If the seeds are all `0`, the default seed is used instead.
42    pub const fn new(seeds: [u32; 4]) -> Self {
43        if (seeds[0] | seeds[1] | seeds[2] | seeds[3]) == 0 {
44            Self::cold_path_default()
45        } else {
46            Self([seeds[0], seeds[1], seeds[2], seeds[3]])
47        }
48    }
49
50    /// Returns a seeded `XorShift128` generator from the given 8-bit seed,
51    /// unchecked.
52    ///
53    /// The seeds must not be all `0`, otherwise every result will also be `0`.
54    ///
55    /// # Panics
56    /// Panics in debug if the seeds are all `0`.
57    pub const fn new_unchecked(seeds: [u32; 4]) -> Self {
58        debug_assert![(seeds[0] | seeds[1] | seeds[2] | seeds[3]) != 0, "Seeds must be non-zero"];
59        Self(seeds)
60    }
61
62    #[must_use]
63    /// Returns the PRNG's inner state as a raw snapshot.
64    pub const fn inner_state(self) -> [u32; 4] {
65        self.0
66    }
67    /// Restores the PRNG from the given state.
68    pub const fn from_state(state: [u32; 4]) -> Self {
69        Self(state)
70    }
71
72    #[must_use]
73    /// Returns the current random `u64`.
74    pub const fn current_u64(&self) -> u64 {
75        ((self.0[0] as u64) << 32) | (self.0[1] as u64)
76    }
77
78    #[must_use]
79    /// Returns the next random `u64`.
80    // Note how the output of the RNG is computed before updating the state,
81    // unlike on Xoroshiro128pp, for example.
82    pub fn next_u64(&mut self) -> u64 {
83        let t = self.0[3];
84        let mut s = self.0[0];
85        self.0[3] = self.0[2];
86        self.0[2] = self.0[1];
87        self.0[1] = s;
88        s ^= s << 11;
89        s ^= s >> 8;
90        self.0[0] = s ^ t ^ (t >> 19);
91
92        ((self.0[0] as u64) << 32) | (self.0[1] as u64)
93    }
94
95    /// Returns a copy of the next new random state.
96    pub const fn peek_next_state(&self) -> Self {
97        let mut x = self.0;
98
99        let t = x[3];
100        let mut s = x[0];
101        x[3] = x[2];
102        x[2] = x[1];
103        x[1] = s;
104        s ^= s << 11;
105        s ^= s >> 8;
106        x[0] = s ^ t ^ (t >> 19);
107
108        Self(x)
109    }
110
111    /// Returns both the next random state and the `u64` value.
112    pub const fn own_next_u64(self) -> Own<Self, u64> {
113        let s = self.peek_next_state();
114        let v = s.current_u64();
115        Own::new(s, v)
116    }
117}
118
119/// # Extra constructors
120impl XorShift128 {
121    /// Returns a seeded `XorShift128` generator from the given 128-bit seed.
122    ///
123    /// The seeds will be split in little endian order.
124    #[cfg(feature = "split")]
125    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "split")))]
126    pub const fn new1_u128(seed: u128) -> Self {
127        Self::new(Cast(seed).into_u32_le())
128    }
129
130    /// Returns a seeded `XorShift128` generator from the given 2 × 64-bit seeds.
131    ///
132    /// The seeds will be split in little endian order.
133    #[cfg(feature = "split")]
134    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "split")))]
135    pub const fn new2_u64(seeds: [u64; 2]) -> Self {
136        let [x, y] = Cast(seeds[0]).into_u32_le();
137        let [z, a] = Cast(seeds[1]).into_u32_le();
138        Self::new([x, y, z, a])
139    }
140
141    /// Returns a seeded `XorShift128` generator from the given 4 × 32-bit seeds.
142    ///
143    /// This is an alias of [`new`][Self#method.new].
144    pub const fn new4_u32(seeds: [u32; 4]) -> Self {
145        Self::new(seeds)
146    }
147
148    /// Returns a seeded `XorShift128` generator from the given 8 × 16-bit seeds.
149    ///
150    /// The seeds will be joined in little endian order.
151    #[cfg(feature = "join")]
152    #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "join")))]
153    pub const fn new8_u16(seeds: [u16; 8]) -> Self {
154        Self::new([
155            Cast::<u32>::from_u16_le([seeds[0], seeds[1]]),
156            Cast::<u32>::from_u16_le([seeds[2], seeds[3]]),
157            Cast::<u32>::from_u16_le([seeds[4], seeds[5]]),
158            Cast::<u32>::from_u16_le([seeds[6], seeds[7]]),
159        ])
160    }
161
162    /// Returns a seeded `XorShift128` generator from the given 16 × 8-bit seeds.
163    ///
164    /// The seeds will be joined in little endian order.
165    pub const fn new16_u8(seeds: [u8; 16]) -> Self {
166        Self::new([
167            u32::from_le_bytes([seeds[0], seeds[1], seeds[2], seeds[3]]),
168            u32::from_le_bytes([seeds[4], seeds[5], seeds[6], seeds[7]]),
169            u32::from_le_bytes([seeds[8], seeds[9], seeds[10], seeds[11]]),
170            u32::from_le_bytes([seeds[12], seeds[13], seeds[14], seeds[15]]),
171        ])
172    }
173}
174
175#[cfg(feature = "dep_rand_core")]
176#[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "dep_rand_core")))]
177mod impl_rand {
178    use super::XorShift128;
179    use crate::_dep::rand_core::{RngCore, SeedableRng};
180
181    impl RngCore for XorShift128 {
182        /// Returns the next random `u32`,
183        /// from the first 32-bits of `next_u64`.
184        fn next_u32(&mut self) -> u32 {
185            self.next_u64() as u32
186        }
187
188        /// Returns the next random `u64`.
189        fn next_u64(&mut self) -> u64 {
190            self.next_u64()
191        }
192
193        fn fill_bytes(&mut self, dest: &mut [u8]) {
194            let mut i = 0;
195            while i < dest.len() {
196                let random_u64 = self.next_u64();
197                let bytes = random_u64.to_le_bytes();
198                let remaining = dest.len() - i;
199
200                if remaining >= 8 {
201                    dest[i..i + 8].copy_from_slice(&bytes);
202                    i += 8;
203                } else {
204                    dest[i..].copy_from_slice(&bytes[..remaining]);
205                    break;
206                }
207            }
208        }
209    }
210
211    impl SeedableRng for XorShift128 {
212        type Seed = [u8; 16];
213
214        /// When seeded with zero this implementation uses the default seed
215        /// value as the cold path.
216        fn from_seed(seed: Self::Seed) -> Self {
217            let mut seed_u32s = [0u32; 4];
218            if seed == [0; 16] {
219                Self::cold_path_default()
220            } else {
221                for i in 0..4 {
222                    seed_u32s[i] = u32::from_le_bytes([
223                        seed[i * 4],
224                        seed[i * 4 + 1],
225                        seed[i * 4 + 2],
226                        seed[i * 4 + 3],
227                    ]);
228                }
229                Self::new_unchecked(seed_u32s)
230            }
231        }
232    }
233}