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}