From 5101709f1c8ea4bd41475f94e7cf2931d420d63c Mon Sep 17 00:00:00 2001 From: aggstam Date: Tue, 5 Dec 2023 19:13:16 +0200 Subject: [PATCH] script/research/dark-forest: document everything --- script/research/dark-forest/src/lib.rs | 69 ++++++++++++++++++++++-- script/research/dark-forest/src/tests.rs | 38 +++++++++++++ 2 files changed, 103 insertions(+), 4 deletions(-) diff --git a/script/research/dark-forest/src/lib.rs b/script/research/dark-forest/src/lib.rs index 458077fa1..53335582d 100644 --- a/script/research/dark-forest/src/lib.rs +++ b/script/research/dark-forest/src/lib.rs @@ -21,22 +21,41 @@ use std::{collections::VecDeque, iter::FusedIterator, mem}; #[cfg(test)] mod tests; -#[derive(Clone, Copy, Debug, PartialEq)] +/// This struct represents a Leaf of a [`DarkTree`], +/// holding this tree node data, along with positional +/// indexes information, based on tree's traversal order. +/// These indexes are only here to enable referencing +/// connected nodes, and are *not* used as pointers by the +/// tree. Creator must ensure they are properly setup. +#[derive(Debug, PartialEq)] struct DarkLeaf { /// Data holded by this leaf data: T, + /// Index showcasing this leaf's position, when all + /// leafs are in order. + index: usize, /// Index showcasing this leaf's parent tree, when all - /// leafs are in order. None indicates this leaf has no - /// parent. + /// leafs are in order. None indicates that this leaf + /// has no parent. parent_index: Option, + /// Vector of indexes showcasing this leaf's children + /// positions, when all leafs are in order. If vector + /// is empty, it indicates that this leaf has no children. + children_indexes: Vec, } impl DarkLeaf { + /// Every leaf is initiated using default indexes. fn new(data: T) -> DarkLeaf { - Self { data, parent_index: None } + Self { data, index: 0, parent_index: None, children_indexes: vec![] } } } +/// This struct represents a Tree using DFS post-order traversal, +/// where when we iterate through the tree, we first process tree +/// node's children, and then the node itself, recursively. +/// Based on this, initial tree node (leaf), known as the root, +/// will always show up at the end of iteration. #[derive(Debug, PartialEq)] struct DarkTree { /// This tree's leaf information, along with its data @@ -46,20 +65,28 @@ struct DarkTree { } impl DarkTree { + /// Initialize a [`DarkTree`], using provided data to + /// generate its root. fn new(data: T, children: Vec>) -> DarkTree { let leaf = DarkLeaf::new(data); Self { leaf, children } } + /// Immutably iterate through the tree, using DFS post-order + /// traversal. fn iter(&self) -> DarkTreeIter<'_, T> { DarkTreeIter { children: std::slice::from_ref(self), parent: None } } + /// Mutably iterate through the tree, using DFS post-order + /// traversal. fn iter_mut(&mut self) -> DarkTreeIterMut<'_, T> { DarkTreeIterMut { children: std::slice::from_mut(self), parent: None, parent_leaf: None } } } +/// Immutable iterator of a [`DarkTree`], performing DFS post-order +/// traversal on the Tree leafs. struct DarkTreeIter<'a, T> { children: &'a [DarkTree], parent: Option>>, @@ -74,6 +101,10 @@ impl Default for DarkTreeIter<'_, T> { impl<'a, T> Iterator for DarkTreeIter<'a, T> { type Item = &'a DarkLeaf; + /// Grab next item iterator visits and return + /// its immutable reference, or recursively + /// create and continue iteration on current + /// leaf's children. fn next(&mut self) -> Option { match self.children.first() { None => match self.parent.take() { @@ -102,6 +133,10 @@ impl<'a, T> Iterator for DarkTreeIter<'a, T> { impl FusedIterator for DarkTreeIter<'_, T> {} +/// Define fusion iteration behavior, allowing +/// us to use the [`DarkTreeIter`] iterator in +/// loops directly, without using .iter() method +/// of [`DarkTree`]. impl<'a, T> IntoIterator for &'a DarkTree { type Item = &'a DarkLeaf; @@ -112,6 +147,8 @@ impl<'a, T> IntoIterator for &'a DarkTree { } } +/// Mutable iterator of a [`DarkTree`], performing DFS post-order +/// traversal on the Tree leafs. struct DarkTreeIterMut<'a, T> { children: &'a mut [DarkTree], parent: Option>>, @@ -127,6 +164,10 @@ impl Default for DarkTreeIterMut<'_, T> { impl<'a, T> Iterator for DarkTreeIterMut<'a, T> { type Item = &'a mut DarkLeaf; + /// Grab next item iterator visits and return + /// its mutable reference, or recursively + /// create and continue iteration on current + /// leaf's children. fn next(&mut self) -> Option { let children = mem::take(&mut self.children); match children.split_first_mut() { @@ -155,6 +196,10 @@ impl<'a, T> Iterator for DarkTreeIterMut<'a, T> { } } +/// Define fusion iteration behavior, allowing +/// us to use the [`DarkTreeIterMut`] iterator +/// in loops directly, without using .iter_mut() +/// method of [`DarkTree`]. impl<'a, T> IntoIterator for &'a mut DarkTree { type Item = &'a mut DarkLeaf; @@ -165,6 +210,9 @@ impl<'a, T> IntoIterator for &'a mut DarkTree { } } +/// Special iterator of a [`DarkTree`], performing DFS post-order +/// traversal on the Tree leafs, consuming each leaf. Since this +/// iterator consumes the tree, it becomes unusable after it's moved. struct DarkTreeIntoIter { children: VecDeque>, parent: Option>>, @@ -179,6 +227,15 @@ impl Default for DarkTreeIntoIter { impl Iterator for DarkTreeIntoIter { type Item = DarkLeaf; + /// Grab next item iterator visits, and return + /// its mutable reference, or recursively + /// create and continue iteration on current + /// leaf's children. + + /// Move next item iterator visits from the tree + /// to the iterator consumer, if it has no children. + /// Otherwise recursively create and continue iteration + /// on current leaf's children, and moving it after them. fn next(&mut self) -> Option { match self.children.pop_front() { None => match self.parent.take() { @@ -210,6 +267,10 @@ impl Iterator for DarkTreeIntoIter { impl FusedIterator for DarkTreeIntoIter {} +/// Define fusion iteration behavior, allowing +/// us to use the [`DarkTreeIntoIter`] .into_iter() +/// method, to consume the [`DarkTree`] and iterate +/// over it. impl IntoIterator for DarkTree { type Item = DarkLeaf; diff --git a/script/research/dark-forest/src/tests.rs b/script/research/dark-forest/src/tests.rs index c26c7a106..a7766dba1 100644 --- a/script/research/dark-forest/src/tests.rs +++ b/script/research/dark-forest/src/tests.rs @@ -18,6 +18,8 @@ use crate::DarkTree; +/// Gereate a predefined [`DarkTree`] along with its +/// expected traversal order. fn generate_tree() -> (DarkTree, Vec) { let tree = DarkTree::new( 5, @@ -36,9 +38,17 @@ fn generate_tree() -> (DarkTree, Vec) { pub fn test_darktree_iterator() { let (tree, traversal_order) = generate_tree(); + // Use [`DarkTree`] iterator to collect current + // data, in order let nums: Vec = tree.iter().map(|x| x.data).collect(); + // Verify iterator collected the data in the expected + // traversal order. assert_eq!(nums, traversal_order); + + // Verify using iterator indexing methods to retrieve + // data from it, returns the expected one, as per + // expected traversal order. assert_eq!(tree.iter().nth(1).unwrap().data, traversal_order[1]); } @@ -46,26 +56,46 @@ pub fn test_darktree_iterator() { fn test_darktree_traversal_order() { let (mut tree, traversal_order) = generate_tree(); + // Loop using the fusion immutable iterator, + // verifying we grab the correct [`DarkLeaf`] + // immutable reference, as per expected + // traversal order. let mut index = 0; for leaf in &tree { assert_eq!(leaf.data, traversal_order[index]); index += 1; } + // Loop using the fusion mutable iterator, + // verifying we grab the correct [`DarkLeaf`] + // mutable reference, as per expected traversal + // order. index = 0; for leaf in &mut tree { assert_eq!(leaf.data, traversal_order[index]); index += 1; } + // Loop using [`DarkTree`] .iter_mut() mutable + // iterator, verifying we grab the correct [`DarkLeaf`] + // mutable reference, as per expected traversal + // order. for (index, leaf) in tree.iter_mut().enumerate() { assert_eq!(leaf.data, traversal_order[index]); } + // Loop using [`DarkTree`] .iter() immutable + // iterator, verifying we grab the correct [`DarkLeaf`] + // immutable reference, as per expected traversal + // order. for (index, leaf) in tree.iter().enumerate() { assert_eq!(leaf.data, traversal_order[index]); } + // Loop using [`DarkTree`] .into_iter() iterator, + // which consumes (moves) the tree, verifying we + // collect the correct [`DarkLeaf`], as per expected + // traversal order. for (index, leaf) in tree.into_iter().enumerate() { assert_eq!(leaf.data, traversal_order[index]); } @@ -75,14 +105,22 @@ fn test_darktree_traversal_order() { fn test_darktree_mut_iterator() { let (mut tree, _) = generate_tree(); + // Loop using [`DarkTree`] .iter_mut() mutable + // iterator, grabing a mutable reference over a + // [`DarkLeaf`], and mutating its inner data. for leaf in tree.iter_mut() { leaf.data += 1; } + // Loop using the fusion mutable iterator, + // grabing a mutable reference over a + // [`DarkLeaf`], and mutating its inner data. for leaf in &mut tree { leaf.data += 1; } + // Verify performed mutation actually happened + // on original tree. assert_eq!( tree, DarkTree::new(