kermit_algos/
hash_singleton.rs1use kermit_iters::{HashTrieIterable, HashTrieIterator, JoinIterable};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15enum State {
16 Root,
17 AtValue,
18 Exhausted,
19}
20
21#[derive(Debug, Clone)]
27pub struct SingletonHashTrieIter {
28 #[allow(dead_code)]
32 value: usize,
33 hash: u64,
34 chain: Vec<Vec<usize>>,
39 state: State,
40}
41
42impl SingletonHashTrieIter {
43 pub fn new(value: usize, hash: u64) -> Self {
52 Self {
53 value,
54 hash,
55 chain: vec![vec![value]],
56 state: State::Root,
57 }
58 }
59}
60
61impl HashTrieIterator for SingletonHashTrieIter {
62 fn key(&self) -> Option<u64> {
63 match self.state {
64 | State::AtValue => Some(self.hash),
65 | State::Root | State::Exhausted => None,
66 }
67 }
68
69 fn next(&mut self) -> Option<u64> {
70 if self.state == State::AtValue {
71 self.state = State::Exhausted;
72 }
73 None
74 }
75
76 fn lookup(&mut self, hash: u64) -> bool {
77 if self.state == State::AtValue && hash == self.hash {
78 true
79 } else if self.state == State::AtValue {
80 self.state = State::Exhausted;
81 false
82 } else {
83 false
84 }
85 }
86
87 fn size(&self) -> usize {
88 if self.state == State::AtValue || self.state == State::Root {
89 1
90 } else {
91 0
92 }
93 }
94
95 fn at_end(&self) -> bool { self.state == State::Exhausted }
96
97 fn open(&mut self) -> bool {
98 match self.state {
99 | State::Root => {
100 self.state = State::AtValue;
101 true
102 },
103 | State::AtValue | State::Exhausted => false,
104 }
105 }
106
107 fn up(&mut self) -> bool {
108 match self.state {
109 | State::AtValue | State::Exhausted => {
110 self.state = State::Root;
111 true
112 },
113 | State::Root => false,
114 }
115 }
116
117 fn leaf_tuples(&self) -> Option<&[Vec<usize>]> {
118 if self.state == State::AtValue {
119 Some(self.chain.as_slice())
120 } else {
121 None
122 }
123 }
124}
125
126impl JoinIterable for SingletonHashTrieIter {}
127
128impl HashTrieIterable for SingletonHashTrieIter {
129 fn hash_trie_iter(&self) -> impl HashTrieIterator { self.clone() }
130}
131
132#[cfg(test)]
133mod tests {
134 use {
135 super::*,
136 kermit_iters::{HashStrategy, SipHashStrategy},
137 };
138
139 fn h(value: usize) -> u64 { SipHashStrategy::hash(value) }
144
145 #[test]
146 fn new_starts_at_root() {
147 let it = SingletonHashTrieIter::new(42, h(42));
148 assert_eq!(it.state, State::Root);
149 assert!(it.key().is_none());
150 }
151
152 #[test]
153 fn open_advances_to_value() {
154 let mut it = SingletonHashTrieIter::new(42, h(42));
155 assert!(it.open());
156 assert_eq!(it.key(), Some(h(42)));
157 }
158
159 #[test]
160 fn lookup_matching_hash_succeeds() {
161 let mut it = SingletonHashTrieIter::new(42, h(42));
162 it.open();
163 assert!(it.lookup(h(42)));
164 }
165
166 #[test]
167 fn lookup_other_hash_fails_and_exhausts() {
168 let mut it = SingletonHashTrieIter::new(42, h(42));
169 it.open();
170 assert!(!it.lookup(h(99)));
171 assert!(it.at_end());
172 }
173
174 #[test]
175 fn next_after_open_exhausts() {
176 let mut it = SingletonHashTrieIter::new(42, h(42));
177 it.open();
178 assert!(it.next().is_none());
179 assert!(it.at_end());
180 }
181
182 #[test]
183 fn leaf_tuples_returns_singleton_chain() {
184 let mut it = SingletonHashTrieIter::new(42, h(42));
185 it.open();
186 let chain = it.leaf_tuples().expect("singleton's leaf chain after open");
187 assert_eq!(chain, &[vec![42]]);
188 }
189
190 #[test]
191 fn leaf_tuples_returns_none_before_open() {
192 let it = SingletonHashTrieIter::new(42, h(42));
193 assert!(it.leaf_tuples().is_none());
194 }
195}