devela/num/rand/xorshift/
u8.rs

1// devela::num::rand::xorshift::u8
2//
3//! 8-bit versions of XorShift.
4//
5
6use crate::{xorshift_basis, ConstDefault, Own};
7
8/// The `XorShift8` <abbr title="Pseudo-Random Number Generator">PRNG</abbr>.
9///
10/// It has an 8-bit state and generates 8-bit numbers.
11/// It has poor statistical quality and limited entropy.
12#[must_use]
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub struct XorShift8<const A: usize = 3, const B: usize = 4, const C: usize = 2>(u8);
15
16/// Creates a new PRNG initialized with the default fixed seed.
17impl<const A: usize, const B: usize, const C: usize> Default for XorShift8<A, B, C> {
18    fn default() -> Self {
19        Self::new_unchecked(Self::DEFAULT_SEED)
20    }
21}
22/// Creates a new PRNG initialized with the default fixed seed.
23impl<const A: usize, const B: usize, const C: usize> ConstDefault for XorShift8<A, B, C> {
24    const DEFAULT: Self = Self::new_unchecked(Self::DEFAULT_SEED);
25}
26
27// private associated items
28impl<const A: usize, const B: usize, const C: usize> XorShift8<A, B, C> {
29    const DEFAULT_SEED: u8 = 0xDE;
30
31    #[cold] #[allow(dead_code)] #[rustfmt::skip]
32    const fn cold_path_default() -> Self { Self::new_unchecked(Self::DEFAULT_SEED) }
33}
34
35impl<const A: usize, const B: usize, const C: usize> XorShift8<A, B, C> {
36    /// Returns a seeded `XorShift8` generator from the given 8-bit seed.
37    ///
38    /// If the seed is `0`, the default seed is used instead.
39    ///
40    /// # Panics
41    /// Panics in debug if either `A`, `B` or `C` are < 1 or > 7.
42    pub const fn new(seed: u8) -> Self {
43        debug_assert![A > 0 && A <= 7];
44        debug_assert![B > 0 && A <= 7];
45        debug_assert![C > 0 && A <= 7];
46        if seed == 0 {
47            Self::cold_path_default()
48        } else {
49            Self(seed)
50        }
51    }
52
53    /// Returns a seeded `XorShift8` generator from the given 8-bit seed,
54    /// unchecked.
55    ///
56    /// The seed must not be `0`, otherwise every result will also be `0`.
57    ///
58    /// # Panics
59    /// Panics in debug if either `A`, `B` or `C` are < 1 or > 7,
60    /// or if the seed is `0`.
61    pub const fn new_unchecked(seed: u8) -> Self {
62        debug_assert![A > 0 && A <= 7];
63        debug_assert![B > 0 && A <= 7];
64        debug_assert![C > 0 && A <= 7];
65        debug_assert![seed != 0, "Seed must be non-zero"];
66        Self(seed)
67    }
68
69    #[must_use]
70    /// Returns the PRNG's inner state as a raw snapshot.
71    pub const fn inner_state(self) -> u8 {
72        self.0
73    }
74    /// Restores the PRNG from the given state.
75    pub const fn from_state(state: u8) -> Self {
76        Self(state)
77    }
78
79    /// Returns the current random `u8`.
80    #[must_use]
81    pub const fn current_u8(&self) -> u8 {
82        self.0
83    }
84
85    /// Updates the state and returns the next random `u8`.
86    ///
87    pub fn next_u8(&mut self) -> u8 {
88        let mut x = self.0;
89        xorshift_basis!(x, 0, (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, 0, (A, B, C));
98        Self(x)
99    }
100
101    /// Returns both the next random state and the `u8` value.
102    pub const fn own_next_u8(self) -> Own<Self, u8> {
103        let s = self.peek_next_state();
104        let v = s.current_u8();
105        Own::new(s, v)
106    }
107}
108
109/// # Extra constructors
110impl<const A: usize, const B: usize, const C: usize> XorShift8<A, B, C> {
111    /// Returns a seeded `XorShift8` generator from the given 8-bit seed.
112    ///
113    /// This is an alias of [`new`][Self#method.new].
114    pub const fn new1_u8(seed: u8) -> Self {
115        Self::new(seed)
116    }
117}
118
119#[cfg(feature = "dep_rand_core")]
120#[cfg_attr(feature = "nightly_doc", doc(cfg(feature = "dep_rand_core")))]
121mod impl_rand {
122    use super::XorShift8;
123    use crate::_dep::rand_core::{RngCore, SeedableRng};
124
125    impl<const A: usize, const B: usize, const C: usize> RngCore for XorShift8<A, B, C> {
126        /// Returns the next 4 × random `u8` combined as a single `u32`.
127        fn next_u32(&mut self) -> u32 {
128            u32::from_le_bytes([self.next_u8(), self.next_u8(), self.next_u8(), self.next_u8()])
129        }
130
131        /// Returns the next 8 × random `u8` combined as a single `u64`.
132        fn next_u64(&mut self) -> u64 {
133            u64::from_le_bytes([
134                self.next_u8(),
135                self.next_u8(),
136                self.next_u8(),
137                self.next_u8(),
138                self.next_u8(),
139                self.next_u8(),
140                self.next_u8(),
141                self.next_u8(),
142            ])
143        }
144
145        fn fill_bytes(&mut self, dest: &mut [u8]) {
146            for byte in dest {
147                *byte = self.next_u8();
148            }
149        }
150    }
151
152    impl<const A: usize, const B: usize, const C: usize> SeedableRng for XorShift8<A, B, C> {
153        type Seed = [u8; 1];
154
155        /// When seeded with zero this implementation uses the default seed
156        /// value as the cold path.
157        fn from_seed(seed: Self::Seed) -> Self {
158            if seed[0] == 0 {
159                Self::cold_path_default()
160            } else {
161                Self::new_unchecked(seed[0])
162            }
163        }
164    }
165}