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}