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