Skip to main content

rusty_alto/
set_trie.rs

1//! Trie index for matching tuples against sets of allowed keys.
2
3use crate::FxHashMap;
4use std::hash::{BuildHasher, Hash};
5
6/// Trie from key sequences to values with efficient set-constrained traversal.
7///
8/// The main query is: given one set of allowed keys per depth, visit all values
9/// whose key tuple chooses an allowed key at every depth. At each trie node the
10/// traversal iterates over whichever side is smaller: the allowed set at this
11/// depth or the actually present outgoing trie edges.
12#[derive(Clone, Debug)]
13pub struct SetTrie<K, V> {
14    next: FxHashMap<K, SetTrie<K, V>>,
15    value: Option<V>,
16}
17
18impl<K, V> Default for SetTrie<K, V> {
19    fn default() -> Self {
20        Self {
21            next: FxHashMap::default(),
22            value: None,
23        }
24    }
25}
26
27impl<K, V> SetTrie<K, V>
28where
29    K: Clone + Eq + Hash,
30{
31    /// Create an empty trie.
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Return the value for `key`, inserting one from `make` if absent.
37    pub fn get_or_insert_with(&mut self, key: &[K], make: impl FnOnce() -> V) -> &mut V {
38        let mut node = self;
39        for part in key {
40            node = node.next.entry(part.clone()).or_default();
41        }
42        node.value.get_or_insert_with(make)
43    }
44
45    /// Return the value for `key`, if present.
46    pub fn get(&self, key: &[K]) -> Option<&V> {
47        let mut node = self;
48        for part in key {
49            node = node.next.get(part)?;
50        }
51        node.value.as_ref()
52    }
53
54    /// Visit every value whose key tuple is accepted by `key_sets`.
55    ///
56    /// `key_sets[d]` is the set of allowed keys at depth `d`. Values stored at
57    /// shorter or longer key lengths are not visited.
58    pub fn for_each_value_for_key_sets<S>(&self, key_sets: &[S], mut out: impl FnMut(&V))
59    where
60        S: KeySet<K>,
61    {
62        self.for_each_value_for_key_sets_at(0, key_sets, &mut out);
63    }
64
65    /// Visit every value whose key tuple starts with `prefix` and whose
66    /// remaining keys are accepted by `key_sets`.
67    pub fn for_each_value_for_prefix_and_key_sets<S>(
68        &self,
69        prefix: &[K],
70        key_sets: &[S],
71        mut out: impl FnMut(&V),
72    ) where
73        S: KeySet<K>,
74    {
75        let mut node = self;
76        for part in prefix {
77            let Some(next) = node.next.get(part) else {
78                return;
79            };
80            node = next;
81        }
82        node.for_each_value_for_key_sets_at(0, key_sets, &mut out);
83    }
84
85    fn for_each_value_for_key_sets_at<S>(
86        &self,
87        depth: usize,
88        key_sets: &[S],
89        out: &mut dyn FnMut(&V),
90    ) where
91        S: KeySet<K>,
92    {
93        if depth == key_sets.len() {
94            if let Some(value) = &self.value {
95                out(value);
96            }
97            return;
98        }
99
100        let keys = &key_sets[depth];
101        if keys.len() < self.next.len() {
102            keys.for_each(&mut |key| {
103                if let Some(next) = self.next.get(key) {
104                    next.for_each_value_for_key_sets_at(depth + 1, key_sets, out);
105                }
106            });
107        } else {
108            for (key, next) in &self.next {
109                if keys.contains(key) {
110                    next.for_each_value_for_key_sets_at(depth + 1, key_sets, out);
111                }
112            }
113        }
114    }
115}
116
117/// A set-like collection that can drive [`SetTrie`] traversal.
118pub trait KeySet<K> {
119    /// Number of keys in the set.
120    fn len(&self) -> usize;
121
122    /// Return whether the set contains no keys.
123    fn is_empty(&self) -> bool {
124        self.len() == 0
125    }
126
127    /// Return whether the set contains `key`.
128    fn contains(&self, key: &K) -> bool;
129
130    /// Visit every key in the set.
131    fn for_each(&self, out: &mut dyn FnMut(&K));
132}
133
134impl<K, S> KeySet<K> for hashbrown::HashSet<K, S>
135where
136    K: Eq + Hash,
137    S: BuildHasher,
138{
139    fn len(&self) -> usize {
140        self.len()
141    }
142
143    fn contains(&self, key: &K) -> bool {
144        self.contains(key)
145    }
146
147    fn for_each(&self, out: &mut dyn FnMut(&K)) {
148        for key in self {
149            out(key);
150        }
151    }
152}
153
154impl<K, T> KeySet<K> for &T
155where
156    T: KeySet<K> + ?Sized,
157{
158    fn len(&self) -> usize {
159        (**self).len()
160    }
161
162    fn contains(&self, key: &K) -> bool {
163        (**self).contains(key)
164    }
165
166    fn for_each(&self, out: &mut dyn FnMut(&K)) {
167        (**self).for_each(out);
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174    use crate::FxHashSet;
175
176    #[test]
177    fn visits_values_matching_key_sets() {
178        let mut trie = SetTrie::new();
179        trie.get_or_insert_with(&[1, 2], Vec::new).push("a");
180        trie.get_or_insert_with(&[1, 3], Vec::new).push("b");
181        trie.get_or_insert_with(&[4, 2], Vec::new).push("c");
182
183        let first = FxHashSet::from_iter([1]);
184        let second = FxHashSet::from_iter([2, 3]);
185        let mut values = Vec::new();
186        trie.for_each_value_for_key_sets(&[&first, &second], |found| {
187            values.extend(found.iter().copied());
188        });
189        values.sort_unstable();
190
191        assert_eq!(values, vec!["a", "b"]);
192    }
193
194    #[test]
195    fn visits_values_matching_prefix_and_key_sets() {
196        let mut trie = SetTrie::new();
197        trie.get_or_insert_with(&[1, 2], Vec::new).push("a");
198        trie.get_or_insert_with(&[1, 3], Vec::new).push("b");
199        trie.get_or_insert_with(&[4, 2], Vec::new).push("c");
200
201        let second = FxHashSet::from_iter([2, 3]);
202        let mut values = Vec::new();
203        trie.for_each_value_for_prefix_and_key_sets(&[1], &[&second], |found| {
204            values.extend(found.iter().copied());
205        });
206        values.sort_unstable();
207
208        assert_eq!(values, vec!["a", "b"]);
209    }
210}