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}