devela/num/rand/xorshift/
u32.rs

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