devela/num/rand/
xabc.rs

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