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