Skip to main content

devela/num/prob/rand/prng/
xabc.rs

1// devela::num::prob::rand::xabc
2//
3//!
4//
5
6use crate::{ConstInit, Own, whilst};
7use crate::{Infallible, InfallibleResult, RandQualities, RandSeedable, RandTry};
8
9#[doc = crate::_tags!(rand)]
10/// X ABC <abbr title="Pseudo-Random Number Generator">PRNG</abbr> for 8-bit devices.
11#[doc = crate::_doc_meta!{location("num/prob/rand")}]
12///
13/// It has a 32-bit state and generates 8-bit numbers.
14///
15/// This is a small PRNG, experimentally verified to have at least a 50 million
16/// byte period by generating 50 million bytes and observing that there were no
17/// overapping sequences and repeats.
18///
19/// This generator passes serial correlation, entropy, Monte Carlo Pi value,
20/// arithmetic mean, and many other statistical tests. This generator may have a
21/// period of up to 2^32, but this has not been verified.
22///
23/// By XORing 3 bytes into the a, b, and c registers, you can add in entropy
24/// from an external source easily.
25///
26/// This generator is free to use, but is not suitable for cryptography due to
27/// its short period (by cryptographic standards) and simple construction.
28/// No attempt was made to make this generator suitable for cryptographic use.
29///
30/// Due to the use of a constant counter, the generator should be resistant to
31/// latching up. A significant performance gain is had in that the x variable is
32/// only ever incremented.
33///
34/// Only 4 bytes of ram are needed for the internal state, and generating a byte
35/// requires 3 XORs, 2 ADDs, one bit shift right, and one increment. Difficult
36/// or slow operations like multiply, etc were avoided for maximum speed on
37/// ultra low power devices.
38///
39/// It has a period of 487,780,609 from a zeroed state.
40///
41#[doc = crate::_doc_vendor!("Xabc")]
42#[must_use]
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub struct Xabc {
45    a: u8,
46    b: u8,
47    c: u8,
48    x: u8,
49}
50
51/// Creates a new PRNG initialized with the default fixed seed.
52impl Default for Xabc {
53    fn default() -> Self {
54        Self::INIT
55    }
56}
57/// Creates a new PRNG initialized with the default fixed seed.
58impl ConstInit for Xabc {
59    const INIT: Self = Self::new(Self::DEFAULT_SEED);
60}
61
62// private associated items
63impl Xabc {
64    #[doc(hidden)]
65    pub const DEFAULT_SEED: [u8; 3] = [0xDE, 0xFA, 0x17];
66}
67
68impl Xabc {
69    /// Returns a seeded `Xabc` generator from the given 3 × 8-bit seeds.
70    pub const fn new(seeds: [u8; 3]) -> Self {
71        let a = seeds[0];
72        let b = seeds[1];
73        let c = seeds[2];
74        let x = 1;
75        let a = a ^ c ^ x;
76        let b = b.wrapping_add(a);
77        let c = c.wrapping_add(b >> 1) ^ a;
78        Self { a, b, c, x }
79    }
80
81    /// Reseeds the generator from the given 3 × 8-bit seeds.
82    pub const fn reseed(&mut self, seeds: [u8; 3]) {
83        // XOR new entropy into key state
84        self.a ^= seeds[0];
85        self.b ^= seeds[1];
86        self.c ^= seeds[2];
87
88        self.x += 1;
89        self.a = self.a ^ self.c ^ self.x;
90        self.b = self.b.wrapping_add(self.a);
91        self.c = self.c.wrapping_add(self.b >> 1) ^ self.a;
92    }
93
94    #[must_use]
95    /// Returns the PRNG's inner state as a raw snapshot.
96    pub const fn inner_state(self) -> [u8; 4] {
97        [self.a, self.b, self.c, self.x]
98    }
99    /// Restores the PRNG from the given state.
100    pub const fn from_state(state: [u8; 4]) -> Self {
101        Self { a: state[0], b: state[1], c: state[2], x: state[3] }
102    }
103
104    /// Returns the current random `u8`.
105    #[must_use]
106    pub const fn current_u8(&self) -> u8 {
107        self.c
108    }
109    /// Advances the state and returns the next random `u8`.
110    #[must_use]
111    pub const fn next_u8(&mut self) -> u8 {
112        // x is incremented every round and is not affected by any other variable
113        self.x = self.x.wrapping_add(1);
114        // note the mix of addition and XOR
115        self.a = self.a ^ self.c ^ self.x;
116        // And the use of very few instructions
117        self.b = self.b.wrapping_add(self.a);
118        // the right shift is to ensure that high-order bits from b can affect
119        // low order bits of other variables
120        self.c = self.c.wrapping_add(self.b >> 1) ^ self.a;
121        self.c
122    }
123
124    /// Returns a copy of the next new random state.
125    pub const fn peek_next_state(&self) -> Self {
126        let [mut a, mut b, mut c, mut x] = [self.a, self.b, self.c, self.x];
127        x += 1;
128        a = a ^ c ^ x;
129        b = b.wrapping_add(a);
130        c = c.wrapping_add(b >> 1) ^ a;
131        Self { a, b, c, x }
132    }
133
134    /// Returns both the next random state and the `u8` value.
135    pub const fn own_next_u8(self) -> Own<Self, u8> {
136        let s = self.peek_next_state();
137        let v = s.current_u8();
138        Own::new(s, v)
139    }
140    /// Fills the buffer with generated bytes.
141    pub const fn fill_bytes(&mut self, buffer: &mut [u8]) {
142        whilst! { i in 0..buffer.len(); {
143            buffer[i] = self.next_u8();
144        }}
145    }
146}
147
148/// # Extras
149impl Xabc {
150    /// Returns a seeded `Xabc` generator from the given 3 × 8-bit seeds.
151    ///
152    /// This is an alias of [`new`][Self#method.new].
153    pub const fn new3_u8(seeds: [u8; 3]) -> Self {
154        Self::new(seeds)
155    }
156    /// Returns the next 2 × random `u8` combined as a single `u16`.
157    pub const fn next_u16(&mut self) -> u16 {
158        u16::from_le_bytes([self.next_u8(), self.next_u8()])
159    }
160    /// Returns the next 4 × random `u8` combined as a single `u32`.
161    pub const fn next_u32(&mut self) -> u32 {
162        u32::from_le_bytes([self.next_u8(), self.next_u8(), self.next_u8(), self.next_u8()])
163    }
164    /// Returns the next 8 × random `u8` combined as a single `u64`.
165    pub const fn next_u64(&mut self) -> u64 {
166        u64::from_le_bytes([
167            self.next_u8(),
168            self.next_u8(),
169            self.next_u8(),
170            self.next_u8(),
171            self.next_u8(),
172            self.next_u8(),
173            self.next_u8(),
174            self.next_u8(),
175        ])
176    }
177}
178
179crate::items! {
180    impl RandTry for Xabc {
181        type Error = Infallible;
182        const RAND_OUTPUT_BITS: u32 = 8;
183        const RAND_STATE_BITS: u32 = 32;
184        const RAND_QUALITIES: RandQualities = RandQualities::WEAK_PRNG;
185        fn rand_try_next_u8(&mut self) -> InfallibleResult<u8> { Ok(self.next_u8()) }
186        fn rand_try_next_u16(&mut self) -> InfallibleResult<u16> { Ok(self.next_u16()) }
187        fn rand_try_next_u32(&mut self) -> InfallibleResult<u32> { Ok(self.next_u32()) }
188        fn rand_try_next_u64(&mut self) -> InfallibleResult<u64> { Ok(self.next_u64()) }
189        fn rand_try_fill_bytes(&mut self, buffer: &mut [u8]) -> InfallibleResult<()> {
190            self.fill_bytes(buffer); Ok(())
191        }
192    }
193    impl RandSeedable for Xabc {
194        type RandSeed = [u8; 3];
195        #[inline(always)]
196        fn rand_from_seed(seed: Self::RandSeed) -> Self { Self::new(seed) }
197    }
198}
199crate::__impl_dep_rand_core!(Xabc);