alazar/misc/
xabc.rs

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