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