1use crate::builder::{QuadraticResidue, RandomSequenceBuilder};
2
3#[derive(Debug, Clone)]
16pub struct RandomSequence<T>
17where
18 T: QuadraticResidue
19{
20 pub config: RandomSequenceBuilder<T>,
22
23 pub(crate) start_index: T,
25 pub(crate) current_index: T,
26 pub(crate) intermediate_offset: T,
27
28 pub(crate) ended: bool,
30}
31
32impl<T> RandomSequence<T>
33where
34 T: QuadraticResidue
35{
36 #[inline]
38 pub fn next(&mut self) -> Option<T> {
39 let next = self.n_internal(self.start_index.wrapping_add(&self.current_index));
40 self.current_index = match self.current_index.checked_add(&T::one()) {
41 Some(v) => {
42 self.ended = false;
43 v
44 },
45 None => {
46 if !self.ended {
47 self.ended = true;
48 self.current_index
49 } else {
50 return None
51 }
52 },
53 };
54 Some(next)
55 }
56
57 #[inline]
62 pub fn wrapping_next(&mut self) -> T {
63 let next = self.n_internal(self.start_index.wrapping_add(&self.current_index));
64 self.current_index = self.current_index.wrapping_add(&T::one());
65 next
66 }
67
68 #[inline]
70 pub fn prev(&mut self) -> Option<T> {
71 self.current_index = match self.current_index.checked_sub(&T::one()) {
73 Some(v) => v,
74 None => return None,
75 };
76 self.ended = false;
77 Some(self.n_internal(self.start_index.wrapping_add(&self.current_index)))
78 }
79
80 #[inline]
82 pub fn wrapping_prev(&mut self) -> T {
83 self.current_index = self.current_index.wrapping_sub(&T::one());
85 self.n_internal(self.start_index.wrapping_add(&self.current_index))
86 }
87
88 #[inline]
90 pub fn n(&self, index: T) -> T {
91 let actual_index = self.start_index.wrapping_add(&index);
92 self.n_internal(actual_index)
93 }
94
95 #[inline(always)]
99 fn n_internal(&self, index: T) -> T {
100 let inner_residue = self.config.permute_qpr(index).wrapping_add(&self.intermediate_offset);
101 self.config.permute_qpr(inner_residue ^ self.config.intermediate_xor)
102 }
103
104 #[inline]
106 pub fn index(&self) -> Option<T> {
107 match self.ended {
108 false => Some(self.current_index),
109 true => None,
110 }
111 }
112
113 #[inline]
115 pub fn exhausted(&self) -> bool {
116 self.ended
117 }
118
119 #[inline]
123 pub fn set_index(&mut self, index: T) {
124 self.current_index = index;
125 self.ended = false;
126 }
127}
128
129macro_rules! impl_unsized_iterator {
130 ($T:ident) => {
131 impl Iterator for RandomSequence<$T> {
132 type Item = $T;
133
134 #[inline]
135 fn next(&mut self) -> Option<Self::Item> {
136 self.next()
137 }
138
139 #[inline]
140 fn size_hint(&self) -> (usize, Option<usize>) {
141 ($T::MAX as usize - self.current_index as usize, None)
142 }
143 }
144 };
145}
146
147macro_rules! impl_exact_size_iterator {
148 ($T:ident) => {
149 impl Iterator for RandomSequence<$T> {
150 type Item = $T;
151
152 #[inline]
153 fn next(&mut self) -> Option<Self::Item> {
154 self.next()
155 }
156
157 #[inline]
158 fn size_hint(&self) -> (usize, Option<usize>) {
159 ($T::MAX as usize + 1 - self.current_index as usize, Some($T::MAX as usize + 1 - self.current_index as usize))
160 }
161 }
162
163 impl ExactSizeIterator for RandomSequence<$T> {}
164 };
165}
166
167impl_exact_size_iterator!(u8);
169impl_exact_size_iterator!(u16);
170#[cfg(target_pointer_width = "64")]
171impl_exact_size_iterator!(u32);
172#[cfg(target_pointer_width = "32")]
173impl_unsized_iterator!(u32);
174impl_unsized_iterator!(u64);
175impl_unsized_iterator!(usize);
176
177impl<T> DoubleEndedIterator for RandomSequence<T>
178where
179 T: QuadraticResidue,
180 RandomSequence<T>: Iterator<Item = T>,
181{
182 #[inline]
183 fn next_back(&mut self) -> Option<Self::Item> {
184 self.prev()
185 }
186}
187
188impl<T> From<RandomSequenceBuilder<T>> for RandomSequence<T>
189where
190 T: QuadraticResidue,
191 RandomSequence<T>: Iterator<Item = T>,
192{
193 #[inline]
194 fn from(value: RandomSequenceBuilder<T>) -> Self {
195 value.into_iter()
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use std::collections::{HashMap, HashSet};
202 use std::vec::Vec;
203
204 use rand::rngs::SysRng;
205 use statrs::distribution::{ChiSquared, ContinuousCDF};
206
207 use super::*;
208
209 fn is_send<T: Send>() {}
210 fn is_sync<T: Sync>() {}
211
212 macro_rules! test_sequence {
213 ($name:ident, $type:ident, $check:literal) => {
214 #[test]
215 fn $name() {
216 let config = RandomSequenceBuilder::<$type>::new(0, 0);
217 let sequence = config.into_iter();
218
219 for (i, num) in std::iter::zip(0..10, sequence.clone()) {
220 assert_eq!(sequence.n(i as $type), num);
221 }
222
223 for (i, num) in std::iter::zip(0..10, sequence.clone().rev()) {
224 assert_eq!(sequence.n($type::MAX.wrapping_sub(i as $type)), num);
225 }
226
227 if ($type::MAX as usize) < $check {
229 let nums_vec: Vec<$type> = config.into_iter().take($check + 10).collect();
230 assert_eq!(nums_vec.len(), ($type::MAX as usize).saturating_add(1));
231 }
232
233 let nums: HashSet<$type> = config.into_iter().take($check).collect();
235 assert_eq!(nums.len(), $check);
236
237 {
239 let mut sequence = config.into_iter();
240
241 assert!(!sequence.exhausted());
243 assert_eq!(sequence.index(), Some(0));
244 let first = sequence.next().unwrap();
245
246 sequence.set_index($type::MAX);
248 assert!(!sequence.exhausted());
249 assert_eq!(sequence.index(), Some($type::MAX));
250 let last = sequence.next().unwrap();
251
252 assert!(sequence.exhausted());
254 assert_eq!(sequence.index(), None);
255 assert!(sequence.next().is_none());
256
257 sequence.set_index($type::MAX);
259 assert!(!sequence.exhausted());
260 assert_eq!(sequence.index(), Some($type::MAX));
261 assert_eq!(sequence.next(), Some(last));
262 assert!(sequence.exhausted()); sequence.set_index(0);
266 assert!(!sequence.exhausted());
267 assert_eq!(sequence.index(), Some(0));
268 assert_eq!(sequence.next(), Some(first));
269 }
270
271 is_send::<RandomSequence<$type>>();
273 is_sync::<RandomSequence<$type>>();
274 }
275 };
276 }
277
278 test_sequence!(test_u8_sequence, u8, 256);
279 test_sequence!(test_u16_sequence, u16, 65536);
280 test_sequence!(test_u32_sequence, u32, 100_000);
281 test_sequence!(test_u64_sequence, u64, 100_000);
282 test_sequence!(test_usize_sequence, usize, 100_000);
283
284 macro_rules! test_exact_size_iterator {
285 ($name:ident, $type:ident) => {
286 #[test]
287 fn $name() {
288 let config = RandomSequenceBuilder::<$type>::new(0, 0);
289 let mut sequence = config.clone().into_iter();
290 assert_eq!(sequence.len(), $type::MAX as usize + 1); sequence.next();
292 assert_eq!(sequence.len(), $type::MAX as usize);
293 sequence.next();
294 assert_eq!(sequence.len(), $type::MAX as usize - 1);
295 sequence.next();
296 assert_eq!(sequence.len(), $type::MAX as usize - 2);
297 sequence.prev();
298 assert_eq!(sequence.len(), $type::MAX as usize - 1);
299
300 sequence.next();
301 sequence.next();
302 sequence.next();
303 assert_eq!(sequence.len(), $type::MAX as usize - 4);
304
305 if sequence.len() <= u16::MAX as usize + 1 {
307 let remaining_len = sequence.len();
308 let remaining_items: Vec<_> = sequence.collect();
309 assert_eq!(remaining_len, remaining_items.len());
310
311 let sequence = config.into_iter();
313 assert_eq!(sequence.len(), $type::MAX as usize + 1);
314 let all_items: Vec<_> = sequence.collect();
315 assert_eq!(all_items.len(), $type::MAX as usize + 1);
316 assert_eq!(all_items.len(), remaining_items.len() + 5);
317 }
318 }
319 };
320 }
321
322 test_exact_size_iterator!(test_u8_exact_size_iterator, u8);
323 test_exact_size_iterator!(test_u16_exact_size_iterator, u16);
324 #[cfg(target_pointer_width = "64")]
325 test_exact_size_iterator!(test_u32_exact_size_iterator, u32);
326
327 macro_rules! test_distribution {
328 ($name:ident, $type:ident, $check:literal) => {
329 #[ignore] #[test]
331 fn $name() {
332 const BUCKETS: usize = 100;
333 let config = RandomSequenceBuilder::<$type>::rand(&mut SysRng);
334
335 let mut data_buckets: HashMap<usize, usize> = HashMap::with_capacity(BUCKETS + 1);
338 config
339 .into_iter()
340 .take($check)
341 .map(|i| ((i as f64 / $type::MAX as f64) * BUCKETS as f64) as usize)
342 .for_each(|i| *data_buckets.entry(i).or_insert(0) += 1);
343 let data_buckets: Vec<f64> = (0..=BUCKETS)
344 .map(|i| *data_buckets.get(&i).unwrap_or(&0) as f64)
345 .collect();
346
347 let mut uniform_buckets: Vec<f64> = (0..BUCKETS)
351 .map(|_| ($check as f64 / BUCKETS as f64))
352 .collect();
353 uniform_buckets.push($check as f64 / $type::MAX as f64); assert_eq!(data_buckets.len(), uniform_buckets.len(), "Data and uniform buckets logic issue.");
357 let chi_squared = std::iter::zip(data_buckets.iter(), uniform_buckets.iter())
358 .map(|(x, e)| (x - e).powi(2) / e)
359 .sum::<f64>();
360
361 let chi_dist = ChiSquared::new((BUCKETS - 1) as f64).unwrap();
363 let p_value = 1.0 - chi_dist.cdf(chi_squared);
364
365 assert!(p_value > 0.05, "Unexpectedly rejected the null hypothesis with high probability. stat: {}, p: {}", chi_squared, p_value);
369 }
370 };
371 }
372
373 test_distribution!(test_u8_distribution, u8, 256);
374 test_distribution!(test_u16_distribution, u16, 65536);
375 test_distribution!(test_u32_distribution, u32, 100_000);
376 test_distribution!(test_u64_distribution, u64, 100_000);
377 test_distribution!(test_usize_distribution, usize, 100_000);
378}