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