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 fn from(value: RandomSequenceBuilder<T>) -> Self {
194 value.into_iter()
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use std::collections::{HashMap, HashSet};
201 use std::vec::Vec;
202
203 use rand::rngs::OsRng;
204 use statrs::distribution::{ChiSquared, ContinuousCDF};
205
206 use super::*;
207
208 fn is_send<T: Send>() {}
209 fn is_sync<T: Sync>() {}
210
211 macro_rules! test_sequence {
212 ($name:ident, $type:ident, $check:literal) => {
213 #[test]
214 fn $name() {
215 let config = RandomSequenceBuilder::<$type>::new(0, 0);
216 let sequence = config.into_iter();
217
218 for (i, num) in std::iter::zip(0..10, sequence.clone()) {
219 assert_eq!(sequence.n(i as $type), num);
220 }
221
222 for (i, num) in std::iter::zip(0..10, sequence.clone().rev()) {
223 assert_eq!(sequence.n($type::MAX.wrapping_sub(i as $type)), num);
224 }
225
226 if ($type::MAX as usize) < $check {
228 let nums_vec: Vec<$type> = config.into_iter().take($check + 10).collect();
229 assert_eq!(nums_vec.len(), $type::MAX as usize + 1);
230 }
231
232 let nums: HashSet<$type> = config.into_iter().take($check).collect();
234 assert_eq!(nums.len(), $check);
235
236 {
238 let mut sequence = config.into_iter();
239
240 assert!(!sequence.exhausted());
242 assert_eq!(sequence.index(), Some(0));
243 let first = sequence.next().unwrap();
244
245 sequence.set_index($type::MAX);
247 assert!(!sequence.exhausted());
248 assert_eq!(sequence.index(), Some($type::MAX));
249 let last = sequence.next().unwrap();
250
251 assert!(sequence.exhausted());
253 assert_eq!(sequence.index(), None);
254 assert!(sequence.next().is_none());
255
256 sequence.set_index($type::MAX);
258 assert!(!sequence.exhausted());
259 assert_eq!(sequence.index(), Some($type::MAX));
260 assert_eq!(sequence.next(), Some(last));
261 assert!(sequence.exhausted()); sequence.set_index(0);
265 assert!(!sequence.exhausted());
266 assert_eq!(sequence.index(), Some(0));
267 assert_eq!(sequence.next(), Some(first));
268 }
269
270 is_send::<RandomSequence<$type>>();
272 is_sync::<RandomSequence<$type>>();
273 }
274 };
275 }
276
277 test_sequence!(test_u8_sequence, u8, 256);
278 test_sequence!(test_u16_sequence, u16, 65536);
279 test_sequence!(test_u32_sequence, u32, 100_000);
280 test_sequence!(test_u64_sequence, u64, 100_000);
281 test_sequence!(test_usize_sequence, usize, 100_000);
282
283 macro_rules! test_exact_size_iterator {
284 ($name:ident, $type:ident) => {
285 #[test]
286 fn $name() {
287 let config = RandomSequenceBuilder::<$type>::new(0, 0);
288 let mut sequence = config.clone().into_iter();
289 assert_eq!(sequence.len(), $type::MAX as usize + 1); sequence.next();
291 assert_eq!(sequence.len(), $type::MAX as usize);
292 sequence.next();
293 assert_eq!(sequence.len(), $type::MAX as usize - 1);
294 sequence.next();
295 assert_eq!(sequence.len(), $type::MAX as usize - 2);
296 sequence.prev();
297 assert_eq!(sequence.len(), $type::MAX as usize - 1);
298
299 sequence.next();
300 sequence.next();
301 sequence.next();
302 assert_eq!(sequence.len(), $type::MAX as usize - 4);
303
304 if sequence.len() <= u16::MAX as usize + 1 {
306 let remaining_len = sequence.len();
307 let remaining_items: Vec<_> = sequence.collect();
308 assert_eq!(remaining_len, remaining_items.len());
309
310 let sequence = config.into_iter();
312 assert_eq!(sequence.len(), $type::MAX as usize + 1);
313 let all_items: Vec<_> = sequence.collect();
314 assert_eq!(all_items.len(), $type::MAX as usize + 1);
315 assert_eq!(all_items.len(), remaining_items.len() + 5);
316 }
317 }
318 };
319 }
320
321 test_exact_size_iterator!(test_u8_exact_size_iterator, u8);
322 test_exact_size_iterator!(test_u16_exact_size_iterator, u16);
323 #[cfg(target_pointer_width = "64")]
324 test_exact_size_iterator!(test_u32_exact_size_iterator, u32);
325
326 macro_rules! test_distribution {
327 ($name:ident, $type:ident, $check:literal) => {
328 #[ignore] #[test]
330 fn $name() {
331 const BUCKETS: usize = 100;
332 let config = RandomSequenceBuilder::<$type>::rand(&mut OsRng);
333
334 let mut data_buckets: HashMap<usize, usize> = HashMap::with_capacity(BUCKETS + 1);
337 config
338 .into_iter()
339 .take($check)
340 .map(|i| ((i as f64 / $type::MAX as f64) * BUCKETS as f64) as usize)
341 .for_each(|i| *data_buckets.entry(i).or_insert(0) += 1);
342 let data_buckets: Vec<f64> = (0..=BUCKETS)
343 .map(|i| *data_buckets.get(&i).unwrap_or(&0) as f64)
344 .collect();
345
346 let mut uniform_buckets: Vec<f64> = (0..BUCKETS)
350 .map(|_| ($check as f64 / BUCKETS as f64))
351 .collect();
352 uniform_buckets.push($check as f64 / $type::MAX as f64); assert_eq!(data_buckets.len(), uniform_buckets.len(), "Data and uniform buckets logic issue.");
356 let chi_squared = std::iter::zip(data_buckets.iter(), uniform_buckets.iter())
357 .map(|(x, e)| (x - e).powi(2) / e)
358 .sum::<f64>();
359
360 let chi_dist = ChiSquared::new((BUCKETS - 1) as f64).unwrap();
362 let p_value = 1.0 - chi_dist.cdf(chi_squared);
363
364 assert!(p_value > 0.05, "Unexpectedly rejected the null hypothesis with high probability. stat: {}, p: {}", chi_squared, p_value);
368 }
369 };
370 }
371
372 test_distribution!(test_u8_distribution, u8, 256);
373 test_distribution!(test_u16_distribution, u16, 65536);
374 test_distribution!(test_u32_distribution, u32, 100_000);
375 test_distribution!(test_u64_distribution, u64, 100_000);
376 test_distribution!(test_usize_distribution, usize, 100_000);
377}