devela/num/rand/xorshift/
u8.rs

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