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}