1use num_traits::{AsPrimitive, PrimInt, WrappingAdd, WrappingSub};
2
3use crate::sequence::RandomSequence;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct RandomSequenceBuilder<T>
23where
24 T: QuadraticResidue
25{
26 pub seed_base: T,
28
29 pub seed_offset: T,
31
32 pub init_base: T,
35
36 pub init_offset: T,
39
40 pub prime: T,
42
43 pub intermediate_xor: T,
45}
46
47impl<T> RandomSequenceBuilder<T>
48where
49 T: QuadraticResidue
50{
51 #[inline]
56 pub unsafe fn from_spec(
57 seed_base: T,
58 seed_offset: T,
59 init_base: T,
60 init_offset: T,
61 prime: T,
62 intermediate_xor: T,
63 ) -> Self {
64 Self {
65 seed_base,
66 seed_offset,
67 init_base,
68 init_offset,
69 prime,
70 intermediate_xor,
71 }
72 }
73
74 #[inline(always)]
76 pub(crate) fn permute_qpr(&self, x: T) -> T {
77 if x >= self.prime {
79 return x;
80 }
81
82 let residue = x.residue(self.prime);
87
88 if x <= self.prime >> 1 {
90 residue
91 } else {
92 self.prime - residue
93 }
94 }
95}
96
97impl<T> IntoIterator for RandomSequenceBuilder<T>
98where
99 T: QuadraticResidue,
100 RandomSequence<T>: Iterator<Item = T>,
101{
102 type Item = T;
103 type IntoIter = RandomSequence<T>;
104
105 #[inline]
107 fn into_iter(self) -> Self::IntoIter {
108 let start_index = self.permute_qpr(self.permute_qpr(self.seed_base).wrapping_add(&self.init_base));
109 let intermediate_offset = self.permute_qpr(self.permute_qpr(self.seed_offset).wrapping_add(&self.init_offset));
110
111 RandomSequence {
112 config: self,
113 start_index,
114 current_index: T::zero(),
115 intermediate_offset,
116 ended: false,
117 }
118 }
119}
120
121impl RandomSequenceBuilder<u8> {
122 #[inline]
124 pub fn new(seed_base: u8, seed_offset: u8) -> Self {
125 Self {
126 seed_base,
127 seed_offset,
128 init_base: 167,
129 init_offset: 181,
130 prime: 251,
131 intermediate_xor: 137,
132 }
133 }
134}
135
136impl RandomSequenceBuilder<u16> {
137 #[inline]
139 pub fn new(seed_base: u16, seed_offset: u16) -> Self {
140 Self {
141 seed_base,
142 seed_offset,
143 init_base: 0x682f,
144 init_offset: 0x4679,
145 prime: 65519,
146 intermediate_xor: 0x5bf0,
147 }
148 }
149}
150
151impl RandomSequenceBuilder<u32> {
152 #[inline]
154 pub fn new(seed_base: u32, seed_offset: u32) -> Self {
155 Self {
156 seed_base,
157 seed_offset,
158 init_base: 0x682f0161,
159 init_offset: 0x46790905,
160 prime: 4294967291,
161 intermediate_xor: 0x5bf03635,
162 }
163 }
164}
165
166impl RandomSequenceBuilder<u64> {
167 #[inline]
169 pub fn new(seed_base: u64, seed_offset: u64) -> Self {
170 Self {
171 seed_base,
172 seed_offset,
173 init_base: 0x682f01615bf03635,
174 init_offset: 0x46790905682f0161,
175 prime: 18446744073709551427,
176 intermediate_xor: 0x5bf0363546790905,
177 }
178 }
179}
180
181impl RandomSequenceBuilder<usize> {
182 #[inline]
184 pub fn new(seed_base: usize, seed_offset: usize) -> Self {
185 Self {
186 seed_base,
187 seed_offset,
188 init_base: 0x682f01615bf03635,
189 init_offset: 0x46790905682f0161,
190 prime: 18446744073709551427,
191 intermediate_xor: 0x5bf0363546790905,
192 }
193 }
194}
195
196pub trait QuadraticResidue
197where
198 Self: PrimInt + AsPrimitive<usize> + WrappingAdd + WrappingSub
199{
200 fn residue(self, prime: Self) -> Self;
202}
203
204macro_rules! impl_residue {
205 ($base_type:ident, $larger_type:ident) => {
206 impl QuadraticResidue for $base_type {
207 #[inline(always)]
208 fn residue(self, prime: Self) -> Self {
209 ((self as $larger_type * self as $larger_type) % prime as $larger_type) as Self
210 }
211 }
212 };
213}
214
215impl_residue!(u8, u16);
216impl_residue!(u16, u32);
217impl_residue!(u32, u64);
218impl_residue!(u64, u128);
219#[cfg(target_pointer_width = "64")]
220impl_residue!(usize, u128);
221#[cfg(target_pointer_width = "32")]
222impl_residue!(usize, u64);
223#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
224compile_error!("Unsupported pointer width.");
225
226#[cfg(test)]
227mod tests {
228 use std::collections::hash_map::Entry;
229 use std::collections::HashMap;
230 use std::string::ToString;
231
232 use super::*;
233
234 fn is_3_mod_4(n: u64) -> bool {
236 n % 4 == 3
237 }
238
239 fn is_send<T: Send>() {}
240 fn is_sync<T: Sync>() {}
241
242 macro_rules! test_config {
243 ($name:ident, $type:ident, $check:literal) => {
244 #[test]
245 fn $name() {
246 let config = RandomSequenceBuilder::<$type>::new(0, 0);
247 let config_orig = config.clone();
248
249 let is_prime_res = is_prime::is_prime(&config.prime.to_string());
252 let is_3_mod_4_res = is_3_mod_4(config.prime as u64);
253
254 let mut found_number = config.prime;
255 if !is_prime_res || !is_3_mod_4_res {
256 if found_number % 2 == 0 {
257 found_number -= 1;
258 }
259
260 let mut found_next_prime = false;
262 let mut found_next_3_mod_4 = false;
263
264 while !found_next_prime || !found_next_3_mod_4 {
265 found_number -= 2;
266 found_next_prime = is_prime::is_prime(&found_number.to_string());
267 found_next_3_mod_4 = is_3_mod_4(found_number as u64);
268 }
269 }
270
271 assert!(is_prime_res, "{} is not prime, suggested prime: {}", config.prime, found_number);
272 assert!(is_3_mod_4_res, "{} = 3 mod 4 doesn't hold, suggested prime: {}", config.prime, found_number);
273
274 let sequence = config.into_iter();
276 assert_eq!(sequence.config, config_orig);
277
278 const CHECK: usize = $check;
280 let mut nums = HashMap::<$type, usize>::new();
281 for i in 0..CHECK {
282 let num = config.permute_qpr(i as $type);
283 match nums.entry(num) {
284 Entry::Vacant(v) => {
285 v.insert(i);
286 }
287 Entry::Occupied(o) => {
288 panic!("Duplicate number {} at index {} and {}", num, o.get(), i);
289 }
290 }
291 }
292 assert_eq!(nums.len(), (0..CHECK).len());
293
294 is_send::<RandomSequenceBuilder<$type>>();
296 is_sync::<RandomSequenceBuilder<$type>>();
297 }
298 };
299 }
300
301 test_config!(test_u8_config, u8, 256);
302 test_config!(test_u16_config, u16, 65536);
303 test_config!(test_u32_config, u32, 100_000);
304 test_config!(test_u64_config, u64, 100_000);
305}