devela/num/rand/lgc/
u16.rs

1// devela::num::rand::lgc::u16
2//
3//! 16-bit Linear Congruential Generator
4//
5
6use crate::{ConstDefault, Own};
7
8#[doc = crate::TAG_RAND!()]
9/// A 16-bit <abbr title="Linear Congruential Generator">LCG</abbr>
10/// <abbr title="Pseudo-Random Number Generator">PRNG</abbr>.
11///
12/// Based on original code from Ken Musgrave, 1985, in Graphics Gems II.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Lgc16(u16);
15
16/// Creates a new PRNG initialized with the default fixed seed.
17impl Default for Lgc16 {
18    fn default() -> Self {
19        Self::DEFAULT
20    }
21}
22/// Creates a new PRNG initialized with the default fixed seed.
23impl ConstDefault for Lgc16 {
24    const DEFAULT: Self = Self::new(Self::DEFAULT_SEED);
25}
26
27// Constant defaults for the Lgc16
28impl Lgc16 {
29    const DEFAULT_SEED: u16 = 0xDEFA;
30
31    /// Multiplier.
32    const MUL: u16 = 25173;
33    /// Increment.
34    const INC: u16 = 13849;
35    /// Modulus.
36    const MOD: u16 = 65535;
37}
38
39impl Lgc16 {
40    /// Creates a new `Lgc16` instance with the given seed.
41    #[must_use]
42    pub const fn new(seed: u16) -> Self {
43        Self(seed)
44    }
45
46    /// Reseeds the generator with a new seed.
47    pub fn reseed(&mut self, seed: u16) {
48        self.0 = seed;
49    }
50
51    #[must_use]
52    /// Returns the PRNG's inner state as a raw snapshot.
53    pub const fn inner_state(self) -> u16 {
54        self.0
55    }
56    /// Restores the PRNG from the given state.
57    pub const fn from_state(state: u16) -> Self {
58        Self(state)
59    }
60
61    /// Returns the current seed value.
62    #[must_use]
63    pub const fn current_u16(&self) -> u16 {
64        self.0
65    }
66    /// Advances to the next random `u16` value.
67    #[must_use]
68    pub fn next_u16(&mut self) -> u16 {
69        self.0 = (Self::MUL.wrapping_mul(self.0).wrapping_add(Self::INC)) & Self::MOD;
70        self.0
71    }
72
73    /// Returns a copy of the next state of the generator.
74    #[must_use]
75    pub const fn peek_next_state(&self) -> Self {
76        let x = (Self::MUL.wrapping_mul(self.0).wrapping_add(Self::INC)) & Self::MOD;
77        Self(x)
78    }
79
80    /// Returns both the next state and the `u16` value.
81    pub const fn own_next_u16(self) -> Own<Self, u16> {
82        let s = self.peek_next_state();
83        let v = s.current_u16();
84        Own::new(s, v)
85    }
86}
87
88/// # Extra constructors
89impl Lgc16 {
90    /// Returns a seeded `Lgc16` generator from the given 16-bit seed.
91    ///
92    /// This is an alias of [`new`][Self#method.new].
93    pub const fn new1_u16(seed: u16) -> Self {
94        Self::new(seed)
95    }
96
97    /// Returns a seeded `Lgc16` generator from the given 2 × 8-bit seeds.
98    ///
99    /// The seeds will be joined in little endian order.
100    #[must_use]
101    pub const fn new2_u8(seeds: [u8; 2]) -> Self {
102        Self::new(u16::from_le_bytes(seeds))
103    }
104}
105
106#[cfg(all(feature = "dep_rand_core", feature = "join"))]
107#[cfg_attr(nightly_doc, doc(cfg(all(feature = "dep_rand_core", feature = "join"))))]
108mod impl_rand {
109    use crate::_dep::rand_core::{RngCore, SeedableRng};
110    use crate::{Cast, Lgc16};
111
112    impl RngCore for Lgc16 {
113        /// Returns the next 2 × random `u16` combined as a single `u32`.
114        fn next_u32(&mut self) -> u32 {
115            Cast::<u32>::from_u16_le([self.next_u16(), self.next_u16()])
116        }
117        /// Returns the next 4 × random `u16` combined as a single `u64`.
118        fn next_u64(&mut self) -> u64 {
119            Cast::<u64>::from_u16_le([
120                self.next_u16(),
121                self.next_u16(),
122                self.next_u16(),
123                self.next_u16(),
124            ])
125        }
126        fn fill_bytes(&mut self, dest: &mut [u8]) {
127            let mut i = 0;
128            while i < dest.len() {
129                let random_u16 = self.next_u16();
130                let bytes = random_u16.to_le_bytes();
131                let remaining = dest.len() - i;
132
133                if remaining >= 2 {
134                    dest[i] = bytes[0];
135                    dest[i + 1] = bytes[1];
136                    i += 2;
137                } else {
138                    dest[i] = bytes[0];
139                    i += 1;
140                }
141            }
142        }
143    }
144
145    impl SeedableRng for Lgc16 {
146        type Seed = [u8; 2];
147
148        /// When seeded with zero this implementation uses the default seed
149        /// value as the cold path.
150        fn from_seed(seed: Self::Seed) -> Self {
151            Self::new(u16::from_le_bytes(seed))
152        }
153    }
154}