orx_tree/
tree_node_idx.rs

1use crate::{
2    TreeVariant,
3    subtrees_within::{ClonedSubTreeWithin, CopiedSubTreeWithin, MovedSubTreeWithin},
4};
5use core::fmt::Debug;
6
7pub(crate) const INVALID_IDX_ERROR: &str = "\n
8NodeIdx is not valid for the given tree. Please see the notes and examples of NodeIdx and MemoryPolicy:
9* https://docs.rs/orx-tree/latest/orx_tree/struct.NodeIdx.html
10* https://docs.rs/orx-tree/latest/orx_tree/trait.MemoryPolicy.html
11
12Specifically, see the example in the following chapter to prevent invalid indices:
13* https://docs.rs/orx-tree/latest/orx_tree/trait.MemoryPolicy.html#lazy-memory-claim-preventing-invalid-indices
14\n";
15
16/// An index associated only with the node it is created for.
17///
18/// * Similar to usize for an array, a `NodeIdx` provides direct constant time access to the
19///   node it is created for.
20///   Therefore, node indices are crucial for efficiency of certain programs.
21/// * Unlike usize for an array, a `NodeIdx` is specific which provides additional safety features.
22///   * A node index is specific to only one node that it is created for, it can never return another node.
23///   * If we create a node index from one tree and use it on another tree, we get an error ([`OutOfBounds`]).
24///   * If we create a node index for a node, then we remove this node from the tree, and then we use
25///     the index, we get an error ([`RemovedNode`]).
26///   * If we create a node index for a node, then the nodes of the tree are reorganized to reclaim memory,
27///     we get an error ([`ReorganizedCollection`]) when we try to use the node index.
28///     This error is due to an implicit operation which is undesirable.
29///     However, we can conveniently avoid such errors using [`Auto`] and [`Lazy`] memory reclaim policies
30///     together. Please see the notes and examples in the [`MemoryPolicy`].
31///
32/// [`OutOfBounds`]: crate::NodeIdxError::OutOfBounds
33/// [`RemovedNode`]: crate::NodeIdxError::RemovedNode
34/// [`ReorganizedCollection`]: crate::NodeIdxError::ReorganizedCollection
35/// [`Auto`]: crate::Auto
36/// [`Lazy`]: crate::Lazy
37/// [`MemoryPolicy`]: crate::MemoryPolicy
38///
39/// # Collecting Node Indices
40///
41/// There are three ways to get the index of a node.
42///
43/// ## 1. During Growth
44///
45/// We can add child nodes by [`push_child`], [`push_children`] and [`extend_children`] methods.
46/// These methods return the indices of the created nodes.
47///
48/// Similarly, horizontal growth methods [`push_sibling`], [`push_siblings`] and [`extend_siblings`]
49/// also return the indices of new nodes.
50///
51/// [`push_child`]: crate::NodeMut::push_child
52/// [`push_children`]: crate::NodeMut::push_children
53/// [`extend_children`]: crate::NodeMut::extend_children
54/// [`push_sibling`]: crate::NodeMut::push_sibling
55/// [`push_siblings`]: crate::NodeMut::push_siblings
56/// [`extend_siblings`]: crate::NodeMut::extend_siblings
57///
58/// **adding a single child: push_child**
59///
60/// ```
61/// use orx_tree::*;
62///
63/// //      1
64/// //     ╱ ╲
65/// //    ╱   ╲
66/// //   2     3
67///
68/// let mut tree = DynTree::new(1);
69///
70/// let mut root = tree.root_mut();
71///
72/// let id2 = root.push_child(2);
73/// let id3 = root.push_child(3);
74///
75/// // use id3 to directly access node 3
76/// let n3 = tree.node(&id3);
77/// assert_eq!(n3.data(), &3);
78/// ```
79///
80/// **adding a constant number of children: push_children**
81///
82/// ```
83/// use orx_tree::*;
84///
85/// //       1
86/// //      ╱|╲
87/// //     ╱ | ╲
88/// //    ╱ ╱╲  ╲
89/// //   2 3  4  5
90///
91/// let mut tree = DynTree::new(1);
92///
93/// let mut root = tree.root_mut();
94///
95/// let [id2, id3] = root.push_children([2, 3]);
96///
97/// let [id4, id5] = root.push_children([4, 5]);
98/// ```
99///
100/// **adding a variable number of children: extend_children**
101///
102/// ```
103/// use orx_tree::*;
104///
105/// //       1
106/// //      ╱|╲
107/// //     ╱ | ╲
108/// //    ╱ ╱╲  ╲
109/// //   2 3  4  5
110///
111/// let mut tree = DynTree::new(1);
112///
113/// let mut root = tree.root_mut();
114///
115/// // indices are collected into a vec
116/// let indices: Vec<_> = root.extend_children(2..6).collect();
117///
118/// let id5 = &indices[3];
119/// let n5 = tree.node(&id5);
120/// assert_eq!(n5.data(), &5);
121/// ```
122///
123/// ## 2. From the Node
124///
125/// A node index can be obtained from the node itself using the [`idx`] method.
126/// There are different ways to access the nodes:
127/// * we can traverse the tree ourselves using child and parent methods,
128/// * or we can traverse the tree [`OverNode`].
129///
130/// [`idx`]: crate::NodeRef::idx
131/// [`OverNode`]: crate::traversal::OverNode
132///
133/// ```
134/// use orx_tree::*;
135///
136/// //      1
137/// //     ╱ ╲
138/// //    ╱   ╲
139/// //   2     3
140/// //  ╱ ╲
141/// // 4   5
142///
143/// let mut tree = DynTree::new(1);
144///
145/// let mut root = tree.root_mut();
146///
147/// let [id2, _] = root.push_children([2, 3]);
148///
149/// let mut n2 = tree.node_mut(&id2);
150/// n2.push_children([4, 5]);
151///
152/// // task: access node 5 and get its index
153/// let root = tree.root();
154/// let n2 = root.child(0);
155/// let n5 = n2.child(1);
156/// let id5 = n5.idx();
157///
158/// // now we can use idx5 to directly access node 5
159/// let n5 = tree.node(&id5);
160/// assert_eq!(n5.data(), &5);
161/// assert_eq!(n5.parent(), Some(tree.node(&id2)));
162/// ```
163///
164/// Since we can traverse the node in various ways and access the nodes in various orders,
165/// we can also collect the indices in desired order.
166///
167/// ```
168/// use orx_tree::*;
169///
170/// //      1
171/// //     ╱ ╲
172/// //    ╱   ╲
173/// //   2     3
174/// //  ╱ ╲
175/// // 4   5
176///
177/// let mut tree = DynTree::new(1);
178///
179/// let mut root = tree.root_mut();
180///
181/// let [id2, _] = root.push_children([2, 3]);
182///
183/// let mut n2 = tree.node_mut(&id2);
184/// n2.push_children([4, 5]);
185///
186/// // task: collect all indices in breadth first order
187/// let mut bfs = Bfs::default().over_nodes();
188/// let root = tree.root();
189/// let indices: Vec<_> = root.walk_with(&mut bfs).map(|x| x.idx()).collect();
190///
191/// // or we can use the shorthand:
192/// let indices: Vec<_> = root.indices::<Bfs>().collect();
193///
194/// // now we can use indices to directly access nodes
195/// let id5 = &indices[4];
196/// let n5 = tree.node(&id5);
197/// assert_eq!(n5.data(), &5);
198/// assert_eq!(n5.parent(), Some(tree.node(&id2)));
199/// ```
200///
201/// # Validity of Node Indices
202///
203/// At the time it is created, the node index:
204///
205/// * is valid for the tree the node belongs to,
206/// * is invalid for any other tree:
207///   * `idx.is_valid_for(&other_tree)` => false
208///   * `idx.node(&other_tree)` => panics!!!
209///   * `idx.get_node(&other_tree)` => None
210///   * `idx.try_get_node(&other_tree)` => Err([`OutOfBounds`])
211///
212/// However, it might later become invalid for the original tree due to two reasons.
213///
214/// The first reason is explicit.
215/// If the node is removed from the tree, directly or due to removal of any of its ancestors,
216/// the corresponding index becomes invalid:
217/// * `idx.is_valid_for(&correct_tree)` => false
218/// * `idx.node(&correct_tree)` => panics!!!
219/// * `idx.get_node(&correct_tree)` => None
220/// * `idx.try_get_node(&correct_tree)` => Err([`RemovedNode`])
221///
222/// The second reason is implicit and closely related to [`MemoryPolicy`].
223/// If removals from the tree triggers a memory reclaim operation which reorganizes the nodes of
224/// the tree, all indices cached prior to the reorganization becomes invalid:
225/// * `idx.is_valid_for(&correct_tree)` => false
226/// * `idx.node(&correct_tree)` => panics!!!
227/// * `idx.get_node(&correct_tree)` => None
228/// * `idx.try_get_node(&correct_tree)` => Err([`ReorganizedCollection`])
229///
230/// The implicit invalidation is not desirable and can be avoided by using memory policies,
231/// please see the [`MemoryPolicy`] documentation and examples.
232/// In brief:
233/// * [`Lazy`] policy never leads to implicit invalidation.
234/// * Growth methods never lead to implicit invalidation.
235/// * We can only experience implicit invalidation when we are using [`Auto`] (or auto with threshold)
236///   memory policy and remove nodes from the tree.
237pub struct NodeIdx<V: TreeVariant>(pub(crate) orx_selfref_col::NodeIdx<V>);
238
239impl<V: TreeVariant> NodeIdx<V> {
240    /// Creates a subtree view including this node as the root and all of its descendants with their orientation relative
241    /// to this node.
242    ///
243    /// Consuming the created subtree in methods such as [`push_child_tree_within`] or [`push_sibling_tree_within`] will remove the
244    /// subtree from its current position to the target position of the same tree.
245    ///
246    /// Otherwise, it has no impact on the tree.
247    ///
248    /// [`push_child_tree_within`]: crate::NodeMut::push_child_tree_within
249    /// [`push_sibling_tree_within`]: crate::NodeMut::push_sibling_tree_within
250    pub fn into_subtree_within(&self) -> MovedSubTreeWithin<V> {
251        MovedSubTreeWithin::new(self.clone())
252    }
253
254    /// Creates a subtree view including this node as the root and all of its descendants with their orientation relative
255    /// to this node.
256    ///
257    /// Consuming the created subtree in methods such as [`push_child_tree_within`] or [`push_sibling_tree_within`] will create
258    /// the same subtree structure in the target position with cloned values.
259    /// This subtree remains unchanged.
260    ///
261    /// [`push_child_tree_within`]: crate::NodeMut::push_child_tree_within
262    /// [`push_sibling_tree_within`]: crate::NodeMut::push_sibling_tree_within
263    pub fn as_cloned_subtree_within(&self) -> ClonedSubTreeWithin<V>
264    where
265        V::Item: Clone,
266    {
267        ClonedSubTreeWithin::new(self.clone())
268    }
269
270    /// Creates a subtree view including this node as the root and all of its descendants with their orientation relative
271    /// to this node.
272    ///
273    /// Consuming the created subtree in methods such as [`push_child_tree_within`] or [`push_sibling_tree_within`] will create
274    /// the same subtree structure in the target position with copied values.
275    /// This subtree remains unchanged.
276    ///
277    /// [`push_child_tree_within`]: crate::NodeMut::push_child_tree_within
278    /// [`push_sibling_tree_within`]: crate::NodeMut::push_sibling_tree_within
279    pub fn as_copied_subtree_within(&self) -> CopiedSubTreeWithin<V>
280    where
281        V::Item: Copy,
282    {
283        CopiedSubTreeWithin::new(self.clone())
284    }
285}
286
287impl<V: TreeVariant> Clone for NodeIdx<V> {
288    fn clone(&self) -> Self {
289        Self(self.0.clone())
290    }
291}
292
293impl<V: TreeVariant> PartialEq for NodeIdx<V> {
294    fn eq(&self, other: &Self) -> bool {
295        self.0 == other.0
296    }
297}
298
299impl<V: TreeVariant> Debug for NodeIdx<V> {
300    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
301        write!(f, "{:?}", self.0)
302    }
303}