hashed_permutation/iterator.rs
1use crate::HashedPermutation;
2use std::num::NonZeroU32;
3
4/// An iterator that allows you to iterate over a sequence of permuted numbers with O(1) space.
5pub struct HashedIter {
6 /// The "engine" driving the permutations
7 permutation_engine: HashedPermutation,
8
9 /// The current index that's being iterated on
10 current_idx: u32,
11}
12
13/// The iterator version of the hashed permutation algorithm
14///
15/// This allows you to use an iterator as you would normally.
16///
17/// ```
18/// # use hashed_permutation::HashedIter;
19/// use std::num::NonZeroU32;
20///
21/// let mut iterator = HashedIter::new_with_seed(NonZeroU32::new(5).unwrap(), 100);
22///
23/// for i in iterator {
24/// println!("{}", i);
25/// }
26/// ```
27impl HashedIter {
28 /// Create a new hashed iterator with a given length
29 ///
30 /// This will create an iterator with an underlying `HashedPermutation` engine with a random
31 /// seed. The seed is generated using the standard library's `thread_rng` class.
32 #[cfg(feature = "use-rand")]
33 pub fn new(length: NonZeroU32) -> Self {
34 let permutation_engine = HashedPermutation::new(length);
35
36 Self {
37 permutation_engine,
38 current_idx: 0,
39 }
40 }
41
42 /// Create a new hashed iterator with a given length and a seed value
43 pub fn new_with_seed(length: NonZeroU32, seed: u32) -> Self {
44 let permutation_engine = HashedPermutation::new_with_seed(length, seed);
45
46 Self {
47 permutation_engine,
48 current_idx: 0,
49 }
50 }
51}
52
53impl Iterator for HashedIter {
54 type Item = u32;
55
56 fn next(&mut self) -> Option<Self::Item> {
57 match self.permutation_engine.shuffle(self.current_idx) {
58 Ok(elem) => {
59 self.current_idx += 1;
60 Some(elem)
61 }
62 Err(_) => None,
63 }
64 }
65}
66
67#[cfg(test)]
68mod test {
69 use super::*;
70 use std::collections::HashSet;
71
72 /// A convenient helper method that returns a pair of lengths and seeds (in that order).
73 ///
74 /// This method defines the lengths and the seeds for the test cases, since these are reused
75 /// in the tests, and it's best practice to consolidate them in one place so code is not
76 /// repeated.
77 fn lengths_and_seeds() -> (Vec<NonZeroU32>, Vec<u32>) {
78 let lengths: Vec<NonZeroU32> = vec![100, 5, 13, 128, 249]
79 .iter()
80 .map(|&x| NonZeroU32::new(x).unwrap())
81 .collect();
82 let seeds = vec![100, 5, 13, 128, 249];
83 assert_eq!(lengths.len(), seeds.len());
84 (lengths, seeds)
85 }
86
87 #[test]
88 // This method checks to see that a permutation does not have any collisions and that every
89 // number maps to another unique number. In other words, we are testing to see whether we have
90 // a bijective function.
91 fn test_bijection() {
92 let (lengths, seeds) = lengths_and_seeds();
93
94 for (&length, seed) in lengths.iter().zip(seeds) {
95 let it = HashedIter::new_with_seed(length, seed);
96
97 // Check that each entry doesn't exist
98 // Check that every number is "hit" (as they'd have to be) for a perfect bijection
99 // Check that the number is within range
100 let mut set = HashSet::new();
101
102 for elem in it {
103 let set_result = set.get(&elem);
104
105 // Make sure there are no duplicates
106 assert!(set_result.is_none());
107 set.insert(elem);
108 }
109 // Need to dereference the types into regular integers
110 let mut result: Vec<u32> = set.into_iter().collect();
111 result.sort();
112 let expected: Vec<u32> = (0..length.get()).collect();
113 assert_eq!(expected, result);
114 }
115 }
116}