devela/num/rand/xorshift/
u16.rs

1// devela::num::rand::xorshift::u16
2//
3//! 16-bit version of XorShift.
4//
5
6use crate::{xorshift_basis, ConstDefault, Own};
7
8/// The `XorShift16` <abbr title="Pseudo-Random Number Generator">PRNG</abbr>.
9///
10/// It has a 16-bit state and generates 16-bit numbers.
11///
12/// This is [John Metcalf's 16-bit] (7, 9, 8) version of George Marsaglia's
13/// original [`XorShift32`][super::XorShift32].
14///
15/// [John Metcalf's 16-bit]: https://github.com/impomatic/xorshift798
16#[must_use]
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct XorShift16<
19    const BASIS: usize = 0,
20    const A: usize = 7,
21    const B: usize = 9,
22    const C: usize = 8,
23>(u16);
24
25/// Creates a new PRNG initialized with the default fixed seed.
26impl Default for XorShift16 {
27    fn default() -> Self {
28        Self::DEFAULT
29    }
30}
31/// Creates a new PRNG initialized with the default fixed seed.
32impl ConstDefault for XorShift16 {
33    const DEFAULT: Self = Self::new_unchecked(Self::DEFAULT_SEED);
34}
35
36// private associated items
37impl<const BASIS: usize, const A: usize, const B: usize, const C: usize>
38    XorShift16<BASIS, A, B, C>
39{
40    const DEFAULT_SEED: u16 = 0xDEFA;
41
42    #[cold] #[allow(dead_code)] #[rustfmt::skip]
43    const fn cold_path_default() -> Self { Self::new_unchecked(Self::DEFAULT_SEED) }
44}
45
46impl<const BASIS: usize, const A: usize, const B: usize, const C: usize>
47    XorShift16<BASIS, A, B, C>
48{
49    /// Returns a seeded `XorShift16` generator from the given 16-bit seed.
50    ///
51    /// If the seed is `0`, the default seed is used instead.
52    pub const fn new(seed: u16) -> Self {
53        if seed == 0 {
54            Self::cold_path_default()
55        } else {
56            Self(seed)
57        }
58    }
59
60    /// Returns a seeded `XorShift16` generator from the given 8-bit seed, unchecked.
61    ///
62    /// The seed must not be `0`, otherwise every result will also be `0`.
63    pub const fn new_unchecked(seed: u16) -> Self {
64        debug_assert![seed != 0, "Seed must be non-zero"];
65        Self(seed)
66    }
67
68    #[must_use]
69    /// Returns the PRNG's inner state as a raw snapshot.
70    pub const fn inner_state(self) -> u16 {
71        self.0
72    }
73    /// Restores the PRNG from the given state.
74    pub const fn from_state(state: u16) -> Self {
75        Self(state)
76    }
77
78    /// Returns the current random `u16`.
79    #[must_use]
80    pub const fn current_u16(&self) -> u16 {
81        self.0
82    }
83
84    /// Returns the next random `u16`.
85    ///
86    #[must_use]
87    pub fn next_u16(&mut self) -> u16 {
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 `u16` value.
102    pub const fn own_next_u16(self) -> Own<Self, u16> {
103        let s = self.peek_next_state();
104        let v = s.current_u16();
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    XorShift16<BASIS, A, B, C>
112{
113    /// Returns a seeded `XorShift16` generator from the given 16-bit seed.
114    ///
115    /// This is an alias of [`new`][Self#method.new].
116    pub const fn new1_u16(seed: u16) -> Self {
117        Self::new(seed)
118    }
119
120    /// Returns a seeded `XorShift16` generator from the given 2 × 8-bit seeds.
121    ///
122    /// The seeds will be joined in little endian order.
123    pub const fn new2_u8(seeds: [u8; 2]) -> Self {
124        Self::new(u16::from_le_bytes(seeds))
125    }
126}
127
128#[cfg(all(feature = "dep_rand_core", feature = "join"))]
129#[cfg_attr(feature = "nightly_doc", doc(cfg(all(feature = "dep_rand_core", feature = "join"))))]
130mod impl_rand {
131    use crate::_dep::rand_core::{RngCore, SeedableRng};
132    use crate::{Cast, XorShift16};
133
134    impl<const BASIS: usize, const A: usize, const B: usize, const C: usize> RngCore
135        for XorShift16<BASIS, A, B, C>
136    {
137        /// Returns the next 2 × random `u16` combined as a single `u32`.
138        fn next_u32(&mut self) -> u32 {
139            Cast::<u32>::from_u16_le([self.next_u16(), self.next_u16()])
140        }
141        /// Returns the next 4 × random `u16` combined as a single `u64`.
142        fn next_u64(&mut self) -> u64 {
143            Cast::<u64>::from_u16_le([
144                self.next_u16(),
145                self.next_u16(),
146                self.next_u16(),
147                self.next_u16(),
148            ])
149        }
150        fn fill_bytes(&mut self, dest: &mut [u8]) {
151            let mut i = 0;
152            while i < dest.len() {
153                let random_u16 = self.next_u16();
154                let bytes = random_u16.to_le_bytes();
155                let remaining = dest.len() - i;
156
157                if remaining >= 2 {
158                    dest[i] = bytes[0];
159                    dest[i + 1] = bytes[1];
160                    i += 2;
161                } else {
162                    dest[i] = bytes[0];
163                    i += 1;
164                }
165            }
166        }
167    }
168
169    impl<const BASIS: usize, const A: usize, const B: usize, const C: usize> SeedableRng
170        for XorShift16<BASIS, A, B, C>
171    {
172        type Seed = [u8; 2];
173
174        /// When seeded with zero this implementation uses the default seed
175        /// value as the cold path.
176        fn from_seed(seed: Self::Seed) -> Self {
177            if seed == [0; 2] {
178                Self::cold_path_default()
179            } else {
180                Self::new_unchecked(u16::from_le_bytes(seed))
181            }
182        }
183    }
184}
185
186/// 4 × good triplets for 16-bit xorshift. (243 Bytes)
187///
188/// There are 60 shift triplets with the maximum period 2^16-1. 4 triplets pass
189/// a series of lightweight randomness tests including randomly plotting various
190/// n × n matrices using the high bits, low bits, reversed bits, etc. These are:
191#[doc(hidden)]
192#[rustfmt::skip]
193#[allow(dead_code)]
194pub const XOROSHIFT_16_TRIPLETS: [(u8, u8, u8); 4] = [
195    (6, 7, 13), (7, 9, 8), (7, 9, 13), (9, 7, 13)
196];