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