once/multiindexed/kdtrie.rs
1use std::ops::{Index, IndexMut};
2
3use super::node::Node;
4
5/// A K-dimensional trie data structure that efficiently stores values indexed by multi-dimensional
6/// coordinates.
7///
8/// The difference between `KdTrie` and `MultiIndexed` is that the latter has the dimension as part
9/// of the type, which allows for more type safety. The `KdTrie` itself may be useful in a situation
10/// where the dimension is not known at compile time.
11///
12/// The trie is structured as a tree where each level corresponds to one dimension of the coordinate
13/// space. For example, in a 3D trie, the first level corresponds to the x-coordinate, the second
14/// level to the y-coordinate, and the third level to the z-coordinate. This structure allows for
15/// efficient storage and retrieval of values in a sparse coordinate space.
16///
17/// # Thread Safety
18///
19/// `KdTrie` is designed to be thread-safe and wait-free, allowing concurrent insertions and
20/// retrievals from multiple threads. This is achieved through the use of atomic operations and the
21/// thread-safe properties of the underlying [`TwoEndedGrove`](crate::TwoEndedGrove) data structure.
22///
23/// # Memory Efficiency
24///
25/// The trie only allocates memory for coordinates that are actually used, making it
26/// memory-efficient for sparse data. Each node in the trie is either an inner node (which contains
27/// child nodes) or a leaf node (which contains values).
28///
29/// # Type Parameters
30///
31/// * `V`: The type of values stored in the trie
32pub struct KdTrie<V> {
33 root: Node<V>,
34 dimensions: usize,
35}
36
37impl<V> KdTrie<V> {
38 /// Creates a new `KdTrie` with the specified number of dimensions.
39 ///
40 /// # Parameters
41 ///
42 /// * `dimensions`: The number of dimensions for the trie (must be greater than 0)
43 ///
44 /// # Panics
45 ///
46 /// Panics if `dimensions` is 0.
47 pub fn new(dimensions: usize) -> Self {
48 assert!(dimensions > 0);
49
50 let root = if dimensions == 1 {
51 Node::new_leaf()
52 } else {
53 Node::new_inner()
54 };
55
56 Self { root, dimensions }
57 }
58
59 /// Retrieves a reference to the value at the specified coordinates, if it exists.
60 ///
61 /// # Parameters
62 ///
63 /// * `coords`: A slice of coordinates with length equal to `self.dimensions`
64 ///
65 /// # Returns
66 ///
67 /// * `Some(&V)` if a value exists at the specified coordinates
68 /// * `None` if no value exists at the specified coordinates
69 ///
70 /// # Panics
71 ///
72 /// Panics if the length of `coords` does not match the number of dimensions.
73 pub fn get(&self, coords: &[i32]) -> Option<&V> {
74 assert!(coords.len() == self.dimensions);
75
76 // When's the last time you saw a mutable shared reference?
77 let mut node = &self.root;
78
79 for &coord in coords.iter().take(self.dimensions - 1) {
80 node = unsafe { node.get_child(coord)? };
81 }
82
83 unsafe { node.get_value(coords[self.dimensions - 1]) }
84 }
85
86 /// Inserts a value at the specified coordinates.
87 ///
88 /// This method traverses the trie structure to find the appropriate location
89 /// for the value, creating nodes as needed along the way.
90 ///
91 /// # Parameters
92 ///
93 /// * `coords`: A slice of coordinates with length equal to `self.dimensions`
94 /// * `value`: The value to insert at the specified coordinates
95 ///
96 /// # Panics
97 ///
98 /// Panics if the length of `coords` does not match the number of dimensions.
99 pub fn insert(&self, coords: &[i32], value: V) {
100 assert!(coords.len() == self.dimensions);
101
102 let mut node = &self.root;
103
104 for &coord in coords.iter().take(self.dimensions.saturating_sub(2)) {
105 node = unsafe { node.ensure_child(coord, Node::new_inner()) };
106 }
107 if self.dimensions > 1 {
108 node = unsafe { node.ensure_child(coords[self.dimensions - 2], Node::new_leaf()) };
109 }
110
111 unsafe { node.set_value(coords[self.dimensions - 1], value) };
112 }
113
114 /// Attempts to insert a value at the specified coordinates.
115 ///
116 /// This method traverses the trie structure to find the appropriate location for the value,
117 /// creating nodes as needed along the way.
118 ///
119 /// This method will only insert the value if the coordinate is not already occupied. If the
120 /// coordinate is already occupied, the value is returned in the `Err` variant.
121 ///
122 /// # Parameters
123 ///
124 /// * `coords`: A slice of coordinates with length equal to `self.dimensions`
125 /// * `value`: The value to insert at the specified coordinates
126 ///
127 /// # Returns
128 ///
129 /// * `Ok(())` if the value was successfully inserted
130 /// * `Err(value)` if the coordinate was already occupied, returning the value that we tried to
131 /// insert
132 ///
133 /// # Panics
134 ///
135 /// Panics if the length of `coords` does not match the number of dimensions.
136 ///
137 /// # Examples
138 ///
139 /// ```
140 /// use once::multiindexed::KdTrie;
141 ///
142 /// let trie = KdTrie::<i32>::new(2);
143 ///
144 /// assert_eq!(trie.try_insert(&[-3, 1], 10), Ok(()));
145 /// assert_eq!(trie.try_insert(&[-3, 1], 10), Err(10)); // Coordinate already occupied
146 /// ```
147 pub fn try_insert(&self, coords: &[i32], value: V) -> Result<(), V> {
148 assert!(coords.len() == self.dimensions);
149
150 let mut node = &self.root;
151
152 for &coord in coords.iter().take(self.dimensions.saturating_sub(2)) {
153 node = unsafe { node.ensure_child(coord, Node::new_inner()) };
154 }
155 if self.dimensions > 1 {
156 node = unsafe { node.ensure_child(coords[self.dimensions - 2], Node::new_leaf()) };
157 }
158
159 unsafe { node.try_set_value(coords[self.dimensions - 1], value) }
160 }
161
162 /// Retrieves a mutable reference to the value at the specified coordinates, if it exists.
163 ///
164 /// This method can only be called if we have an exclusive reference to self. When you have
165 /// exclusive access, there's no possibility of concurrent access, so the atomic synchronization
166 /// used by `get` is unnecessary.
167 ///
168 /// # Parameters
169 ///
170 /// * `coords`: A slice of coordinates with length equal to `self.dimensions`
171 ///
172 /// # Returns
173 ///
174 /// * `Some(&mut V)` if a value exists at the specified coordinates
175 /// * `None` if no value exists at the specified coordinates
176 ///
177 /// # Panics
178 ///
179 /// Panics if the length of `coords` does not match the number of dimensions.
180 ///
181 /// # Examples
182 ///
183 /// ```
184 /// use once::multiindexed::KdTrie;
185 ///
186 /// let mut trie = KdTrie::<i32>::new(2);
187 /// trie.insert(&[1, 2], 42);
188 ///
189 /// // Modify the value through a mutable reference
190 /// if let Some(value) = trie.get_mut(&[1, 2]) {
191 /// *value = 100;
192 /// }
193 ///
194 /// assert_eq!(trie.get(&[1, 2]), Some(&100));
195 /// assert_eq!(trie.get_mut(&[0, 0]), None);
196 /// ```
197 pub fn get_mut(&mut self, coords: &[i32]) -> Option<&mut V> {
198 assert!(coords.len() == self.dimensions);
199
200 let mut node = &mut self.root;
201
202 for &coord in coords.iter().take(self.dimensions - 1) {
203 node = unsafe { node.get_child_mut(coord)? };
204 }
205
206 unsafe { node.get_value_mut(coords[self.dimensions - 1]) }
207 }
208
209 pub fn dimensions(&self) -> usize {
210 self.dimensions
211 }
212
213 pub(super) fn root(&self) -> &Node<V> {
214 &self.root
215 }
216
217 pub(super) fn root_mut(&mut self) -> &mut Node<V> {
218 &mut self.root
219 }
220}
221
222impl<V> Drop for KdTrie<V> {
223 fn drop(&mut self) {
224 self.root.drop_level(self.dimensions, 0);
225 }
226}
227
228impl<V: std::fmt::Debug> std::fmt::Debug for KdTrie<V> {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 f.debug_map().entries(self.iter()).finish()
231 }
232}
233
234impl<V: Clone> Clone for KdTrie<V> {
235 fn clone(&self) -> Self {
236 let new_trie = Self::new(self.dimensions);
237 for (coords, value) in self.iter() {
238 new_trie.insert(&coords, value.clone());
239 }
240 new_trie
241 }
242}
243
244impl<V: PartialEq> PartialEq for KdTrie<V> {
245 fn eq(&self, other: &Self) -> bool {
246 self.dimensions == other.dimensions && self.iter().eq(other.iter())
247 }
248}
249
250impl<V: Eq> Eq for KdTrie<V> {}
251
252impl<V: std::hash::Hash> std::hash::Hash for KdTrie<V> {
253 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
254 self.dimensions.hash(state); // This distinguishes empty tries from eachother
255 for (coords, value) in self.iter() {
256 coords.hash(state);
257 value.hash(state);
258 }
259 }
260}
261
262impl<V> Index<&[i32]> for KdTrie<V> {
263 type Output = V;
264
265 fn index(&self, index: &[i32]) -> &Self::Output {
266 self.get(index)
267 .unwrap_or_else(|| panic!("no value at index {index:?}"))
268 }
269}
270
271impl<V> IndexMut<&[i32]> for KdTrie<V> {
272 fn index_mut(&mut self, index: &[i32]) -> &mut Self::Output {
273 self.get_mut(index)
274 .unwrap_or_else(|| panic!("no value at index {index:?}"))
275 }
276}