script/research/dark-forest: document everything

This commit is contained in:
aggstam
2023-12-05 19:13:16 +02:00
parent 71f2c8de64
commit 5101709f1c
2 changed files with 103 additions and 4 deletions

View File

@@ -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<T> {
/// 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<usize>,
/// 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<usize>,
}
impl<T> DarkLeaf<T> {
/// Every leaf is initiated using default indexes.
fn new(data: T) -> DarkLeaf<T> {
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<T> {
/// This tree's leaf information, along with its data
@@ -46,20 +65,28 @@ struct DarkTree<T> {
}
impl<T> DarkTree<T> {
/// Initialize a [`DarkTree`], using provided data to
/// generate its root.
fn new(data: T, children: Vec<DarkTree<T>>) -> DarkTree<T> {
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<T>],
parent: Option<Box<DarkTreeIter<'a, T>>>,
@@ -74,6 +101,10 @@ impl<T> Default for DarkTreeIter<'_, T> {
impl<'a, T> Iterator for DarkTreeIter<'a, T> {
type Item = &'a DarkLeaf<T>;
/// 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<Self::Item> {
match self.children.first() {
None => match self.parent.take() {
@@ -102,6 +133,10 @@ impl<'a, T> Iterator for DarkTreeIter<'a, T> {
impl<T> 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<T> {
type Item = &'a DarkLeaf<T>;
@@ -112,6 +147,8 @@ impl<'a, T> IntoIterator for &'a DarkTree<T> {
}
}
/// Mutable iterator of a [`DarkTree`], performing DFS post-order
/// traversal on the Tree leafs.
struct DarkTreeIterMut<'a, T> {
children: &'a mut [DarkTree<T>],
parent: Option<Box<DarkTreeIterMut<'a, T>>>,
@@ -127,6 +164,10 @@ impl<T> Default for DarkTreeIterMut<'_, T> {
impl<'a, T> Iterator for DarkTreeIterMut<'a, T> {
type Item = &'a mut DarkLeaf<T>;
/// 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<Self::Item> {
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<T> {
type Item = &'a mut DarkLeaf<T>;
@@ -165,6 +210,9 @@ impl<'a, T> IntoIterator for &'a mut DarkTree<T> {
}
}
/// 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<T> {
children: VecDeque<DarkTree<T>>,
parent: Option<Box<DarkTreeIntoIter<T>>>,
@@ -179,6 +227,15 @@ impl<T> Default for DarkTreeIntoIter<T> {
impl<T> Iterator for DarkTreeIntoIter<T> {
type Item = DarkLeaf<T>;
/// 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<Self::Item> {
match self.children.pop_front() {
None => match self.parent.take() {
@@ -210,6 +267,10 @@ impl<T> Iterator for DarkTreeIntoIter<T> {
impl<T> FusedIterator for DarkTreeIntoIter<T> {}
/// Define fusion iteration behavior, allowing
/// us to use the [`DarkTreeIntoIter`] .into_iter()
/// method, to consume the [`DarkTree`] and iterate
/// over it.
impl<T> IntoIterator for DarkTree<T> {
type Item = DarkLeaf<T>;

View File

@@ -18,6 +18,8 @@
use crate::DarkTree;
/// Gereate a predefined [`DarkTree`] along with its
/// expected traversal order.
fn generate_tree() -> (DarkTree<i32>, Vec<i32>) {
let tree = DarkTree::new(
5,
@@ -36,9 +38,17 @@ fn generate_tree() -> (DarkTree<i32>, Vec<i32>) {
pub fn test_darktree_iterator() {
let (tree, traversal_order) = generate_tree();
// Use [`DarkTree`] iterator to collect current
// data, in order
let nums: Vec<i32> = 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(