Skip to main content

devela/num/prob/rand/prng/shift/
u32.rs

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