dark-tree: moved from script/research/dark-forest into src/sdk

This commit is contained in:
aggstam
2023-12-18 12:23:23 +02:00
parent 6bb313db3d
commit 051311507d
8 changed files with 1252 additions and 1303 deletions

View File

@@ -1,3 +0,0 @@
/target
Cargo.lock
rustfmt.toml

View File

@@ -1,13 +0,0 @@
[package]
name = "dark-forest"
version = "0.1.0"
description = "Minimal Tree implementation using DFS post-order traversal to iterate its leafs."
authors = ["Dyne.org foundation <foundation@dyne.org>"]
repository = "https://github.com/darkrenaissance/darkfi"
license = "AGPL-3.0-only"
edition = "2021"
[workspace]
[dependencies]
thiserror = "1.0.50"

View File

@@ -1,45 +0,0 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/// Main result type used by this library.
pub type DarkTreeResult<T> = std::result::Result<T, DarkTreeError>;
/// General library errors.
#[derive(Debug, Clone, thiserror::Error)]
pub enum DarkTreeError {
#[error("Invalid DarkLeaf index found: {0} (Expected: {1}")]
InvalidLeafIndex(usize, usize),
#[error("Invalid DarkLeaf parent index found for leaf: {0}")]
InvalidLeafParentIndex(usize),
#[error("Invalid DarkLeaf children index found for leaf: {0}")]
InvalidLeafChildrenIndexes(usize),
#[error("Invalid DarkTree min capacity found: {0} (Expected: >= 1)")]
InvalidMinCapacity(usize),
#[error("DarkTree min capacity has not been exceeded")]
MinCapacityNotExceeded,
#[error("Invalid DarkTree max capacity found: {0} (Expected: >= {1})")]
InvalidMaxCapacity(usize, usize),
#[error("DarkTree max capacity has been exceeded")]
MaxCapacityExceeded,
}

View File

@@ -1,565 +0,0 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{collections::VecDeque, iter::FusedIterator, mem};
/// Error handling
mod error;
use error::{DarkTreeError, DarkTreeResult};
#[cfg(test)]
mod tests;
/// This struct represents the information hold by a
/// [`DarkTreeLeaf`], namely its 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(Clone, Debug, PartialEq)]
struct DarkLeaf<T>
where
T: Clone,
{
/// Data holded by this leaf
data: T,
/// Index showcasing this leaf's parent tree, when all
/// 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>,
}
/// This struct represents a Leaf of a [`DarkTree`],
/// holding this tree node data, along with its positional
/// index, based on tree's traversal order.
#[derive(Clone, Debug, PartialEq)]
struct DarkTreeLeaf<T>
where
T: Clone,
{
/// Index showcasing this leaf's position, when all
/// leafs are in order.
index: usize,
/// Leaf's data, along with its parent and children
/// indexes information.
info: DarkLeaf<T>,
}
impl<T: std::clone::Clone> DarkTreeLeaf<T> {
/// Every [`DarkTreeLeaf`] is initiated using default indexes.
fn new(data: T) -> DarkTreeLeaf<T> {
Self { index: 0, info: DarkLeaf { data, parent_index: None, children_indexes: vec![] } }
}
/// Set [`DarkTreeLeaf`]'s index
fn set_index(&mut self, index: usize) {
self.index = index;
}
/// Set [`DarkTreeLeaf`]'s parent index
fn set_parent_index(&mut self, parent_index: Option<usize>) {
self.info.parent_index = parent_index;
}
/// Set [`DarkTreeLeaf`]'s children index
fn set_children_indexes(&mut self, children_indexes: Vec<usize>) {
self.info.children_indexes = children_indexes;
}
}
/// 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. It is advised
/// to always execute .build() after finishing setting up the
/// Tree, to properly index it and check its integrity.
#[derive(Debug, PartialEq)]
struct DarkTree<T: std::clone::Clone> {
/// This tree's leaf information, along with its data
leaf: DarkTreeLeaf<T>,
/// Vector containing all tree's branches(children tree)
children: Vec<DarkTree<T>>,
/// Min capacity of the tree, including all children nodes
/// recursively from the root. Since root is always present,
/// min capacity must always be >= 1. This is enforced by
/// the root, so children nodes don't have to set it up.
/// If children nodes children(recursively) make us not exceed
/// that min capacity, we will be able to catch it using
/// .check_min_capacity() or .integrity_check().
min_capacity: usize,
/// Optional max capacity of the tree, including all children
/// nodes recursively from the root. None indicates no
/// capacity restrictions. This is enforced by the root,
/// so children nodes don't have to set it up. If children
/// nodes children(recursively) make us exceed that capacity,
/// we will be able to catch it using .check_max_capacity() or
/// .integrity_check().
max_capacity: Option<usize>,
}
impl<T: std::clone::Clone> DarkTree<T> {
/// Initialize a [`DarkTree`], using provided data to
/// generate its root.
fn new(
data: T,
children: Vec<DarkTree<T>>,
min_capacity: Option<usize>,
max_capacity: Option<usize>,
) -> DarkTree<T> {
// Setup min capacity
let min_capacity = if let Some(min_capacity) = min_capacity {
if min_capacity == 0 {
1
} else {
min_capacity
}
} else {
1
};
let leaf = DarkTreeLeaf::new(data);
Self { leaf, children, min_capacity, max_capacity }
}
/// Build the [`DarkTree`] indexes and perform an
/// integrity check on them. This should be used
/// after we have appended all child nodes, so we
/// don't have to call .index() and .integrity_check()
/// manually.
fn build(&mut self) -> DarkTreeResult<()> {
self.index();
self.integrity_check()
}
/// Build the [`DarkTree`] using .build() and
/// then produce a flattened vector containing
/// all the leafs in DFS post-order traversal order.
fn build_vec(&mut self) -> DarkTreeResult<Vec<DarkLeaf<T>>> {
self.build()?;
Ok(self.iter().cloned().map(|x| x.info).collect())
}
/// Return the count of all [`DarkTree`] leafs.
fn len(&self) -> usize {
self.iter().count()
}
/// Check if configured min capacity have not been exceeded.
fn check_min_capacity(&self) -> DarkTreeResult<()> {
if self.len() < self.min_capacity {
return Err(DarkTreeError::MinCapacityNotExceeded)
}
Ok(())
}
/// Check if configured max capacity have been exceeded.
fn check_max_capacity(&self) -> DarkTreeResult<()> {
if let Some(max_capacity) = self.max_capacity {
if self.len() > max_capacity {
return Err(DarkTreeError::MaxCapacityExceeded)
}
}
Ok(())
}
/// Append a new child node to the [`DarkTree`],
/// if max capacity has not been exceeded. This call
/// doesn't update the indexes, so either .index()
/// or .build() must be called after it.
fn append(&mut self, child: DarkTree<T>) -> DarkTreeResult<()> {
// Check current max capacity
if let Some(max_capacity) = self.max_capacity {
if self.len() + 1 > max_capacity {
return Err(DarkTreeError::MaxCapacityExceeded)
}
}
// Append the new child
self.children.push(child);
Ok(())
}
/// Set [`DarkTree`]'s leaf parent and children indexes,
/// and trigger the setup of its children indexes.
fn set_parent_children_indexes(&mut self, parent_index: Option<usize>) {
// Set our leafs parent index
self.leaf.set_parent_index(parent_index);
// Now recursively, we setup nodes children indexes and keep
// their index in our own children index list
let mut children_indexes = vec![];
for child in &mut self.children {
child.set_parent_children_indexes(Some(self.leaf.index));
children_indexes.push(child.leaf.index);
}
// Set our leafs children indexes
self.leaf.set_children_indexes(children_indexes);
}
/// Setup [`DarkTree`]'s leafs indexes, based on DFS post-order
/// traversal order. This call assumes it was triggered for the
/// root of the tree, which has no parent index.
fn index(&mut self) {
// First we setup each leafs index
for (index, leaf) in self.iter_mut().enumerate() {
leaf.set_index(index);
}
// Now we trigger recursion to setup each nodes rest indexes
self.set_parent_children_indexes(None);
}
/// Verify [`DarkTree`]'s leaf parent and children indexes validity,
/// and trigger the check of its children indexes.
fn check_parent_children_indexes(&self, parent_index: Option<usize>) -> DarkTreeResult<()> {
// Check our leafs parent index
if self.leaf.info.parent_index != parent_index {
return Err(DarkTreeError::InvalidLeafParentIndex(self.leaf.index))
}
// Now recursively, we check nodes children indexes and keep
// their index in our own children index list
let mut children_indexes = vec![];
for child in &self.children {
child.check_parent_children_indexes(Some(self.leaf.index))?;
children_indexes.push(child.leaf.index);
}
// Check our leafs children indexes
if self.leaf.info.children_indexes != children_indexes {
return Err(DarkTreeError::InvalidLeafChildrenIndexes(self.leaf.index))
}
Ok(())
}
/// Verify current [`DarkTree`]'s leafs indexes validity,
/// based on DFS post-order traversal order. Additionally,
/// check that min and max capacities have been properly
/// configured, min capacity has been exceeded and max
/// capacity has not. This call assumes it was triggered
/// for the root of the tree, which has no parent index.
fn integrity_check(&self) -> DarkTreeResult<()> {
// Check current min capacity is valid
if self.min_capacity < 1 {
return Err(DarkTreeError::InvalidMinCapacity(self.min_capacity))
}
// Check currect max capacity is not less than
// current min capacity
if let Some(max_capacity) = self.max_capacity {
if self.min_capacity > max_capacity {
return Err(DarkTreeError::InvalidMaxCapacity(max_capacity, self.min_capacity))
}
}
// Check current min capacity
self.check_min_capacity()?;
// Check current max capacity
self.check_max_capacity()?;
// Check each leaf index
for (index, leaf) in self.iter().enumerate() {
if index != leaf.index {
return Err(DarkTreeError::InvalidLeafIndex(leaf.index, index))
}
}
// Trigger recursion to check each nodes rest indexes
self.check_parent_children_indexes(None)
}
/// 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: std::clone::Clone> {
children: &'a [DarkTree<T>],
parent: Option<Box<DarkTreeIter<'a, T>>>,
}
impl<T: std::clone::Clone> Default for DarkTreeIter<'_, T> {
fn default() -> Self {
DarkTreeIter { children: &[], parent: None }
}
}
impl<'a, T: std::clone::Clone> Iterator for DarkTreeIter<'a, T> {
type Item = &'a DarkTreeLeaf<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() {
Some(parent) => {
// Grab parent's leaf
*self = *parent;
// Its safe to unwrap here as we effectively returned
// to this tree after "pushing" it after its children
let leaf = &self.children.first().unwrap().leaf;
self.children = &self.children[1..];
Some(leaf)
}
None => None,
},
Some(leaf) => {
// Iterate over tree's children/sub-trees
*self = DarkTreeIter {
children: leaf.children.as_slice(),
parent: Some(Box::new(mem::take(self))),
};
self.next()
}
}
}
}
impl<T: std::clone::Clone> 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: std::clone::Clone> IntoIterator for &'a DarkTree<T> {
type Item = &'a DarkTreeLeaf<T>;
type IntoIter = DarkTreeIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
/// Mutable iterator of a [`DarkTree`], performing DFS post-order
/// traversal on the Tree leafs.
struct DarkTreeIterMut<'a, T: std::clone::Clone> {
children: &'a mut [DarkTree<T>],
parent: Option<Box<DarkTreeIterMut<'a, T>>>,
parent_leaf: Option<&'a mut DarkTreeLeaf<T>>,
}
impl<T: std::clone::Clone> Default for DarkTreeIterMut<'_, T> {
fn default() -> Self {
DarkTreeIterMut { children: &mut [], parent: None, parent_leaf: None }
}
}
impl<'a, T: std::clone::Clone> Iterator for DarkTreeIterMut<'a, T> {
type Item = &'a mut DarkTreeLeaf<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() {
None => match self.parent.take() {
Some(parent) => {
// Grab parent's leaf
let parent_leaf = mem::take(&mut self.parent_leaf);
*self = *parent;
parent_leaf
}
None => None,
},
Some((first, rest)) => {
// Setup simplings iteration
self.children = rest;
// Iterate over tree's children/sub-trees
*self = DarkTreeIterMut {
children: first.children.as_mut_slice(),
parent: Some(Box::new(mem::take(self))),
parent_leaf: Some(&mut first.leaf),
};
self.next()
}
}
}
}
/// Define fusion iteration behavior, allowing
/// us to use the [`DarkTreeIterMut`] iterator
/// in loops directly, without using .iter_mut()
/// method of [`DarkTree`].
impl<'a, T: std::clone::Clone> IntoIterator for &'a mut DarkTree<T> {
type Item = &'a mut DarkTreeLeaf<T>;
type IntoIter = DarkTreeIterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
/// 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: std::clone::Clone> {
children: VecDeque<DarkTree<T>>,
parent: Option<Box<DarkTreeIntoIter<T>>>,
}
impl<T: std::clone::Clone> Default for DarkTreeIntoIter<T> {
fn default() -> Self {
DarkTreeIntoIter { children: Default::default(), parent: None }
}
}
impl<T: std::clone::Clone> Iterator for DarkTreeIntoIter<T> {
type Item = DarkTreeLeaf<T>;
/// 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() {
Some(parent) => {
// Continue iteration on parent's simplings
*self = *parent;
self.next()
}
None => None,
},
Some(mut leaf) => {
// If leaf has no children, return it
if leaf.children.is_empty() {
return Some(leaf.leaf)
}
// Push leaf after its children
let mut children: VecDeque<DarkTree<T>> = leaf.children.into();
leaf.children = Default::default();
children.push_back(leaf);
// Iterate over tree's children/sub-trees
*self = DarkTreeIntoIter { children, parent: Some(Box::new(mem::take(self))) };
self.next()
}
}
}
}
impl<T: std::clone::Clone> 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: std::clone::Clone> IntoIterator for DarkTree<T> {
type Item = DarkTreeLeaf<T>;
type IntoIter = DarkTreeIntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
let mut children = VecDeque::with_capacity(1);
children.push_back(self);
DarkTreeIntoIter { children, parent: None }
}
}
/// Auxiliary function to verify provided [`DarkLeaf`] slice is
/// properly bounded and its members indexes are valid.
fn dark_leaf_vec_integrity_check<T: std::clone::Clone>(
leafs: &[DarkLeaf<T>],
min_capacity: Option<usize>,
max_capacity: Option<usize>,
) -> DarkTreeResult<()> {
// Setup min capacity
let min_capacity = if let Some(min_capacity) = min_capacity {
if min_capacity == 0 {
1
} else {
min_capacity
}
} else {
1
};
// Check currect max capacity is not less than
// current min capacity
if let Some(max_capacity) = max_capacity {
if min_capacity > max_capacity {
return Err(DarkTreeError::InvalidMaxCapacity(max_capacity, min_capacity))
}
}
// Check if min capacity have been not exceeded
if leafs.len() < min_capacity {
return Err(DarkTreeError::MinCapacityNotExceeded)
}
// Check if max capacity have been exceeded
if let Some(max_capacity) = max_capacity {
if leafs.len() > max_capacity {
return Err(DarkTreeError::MaxCapacityExceeded)
}
}
// Check each leaf indexes
for (index, leaf) in leafs.iter().enumerate() {
// Parent must have our index in their children
if let Some(parent_index) = leaf.parent_index {
if !leafs[parent_index].children_indexes.contains(&index) {
return Err(DarkTreeError::InvalidLeafChildrenIndexes(parent_index))
}
}
// Children must have its parent set to us
for child_index in &leaf.children_indexes {
// Children must have its parent set to us
match leafs[*child_index].parent_index {
Some(parent_index) => {
if parent_index != index {
return Err(DarkTreeError::InvalidLeafParentIndex(*child_index))
}
}
None => return Err(DarkTreeError::InvalidLeafParentIndex(*child_index)),
}
}
}
Ok(())
}

View File

@@ -1,677 +0,0 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{dark_leaf_vec_integrity_check, DarkLeaf, DarkTree, DarkTreeLeaf, DarkTreeResult};
/// Gereate a predefined [`DarkTree`] along with its
/// expected traversal order.
///
/// Tree structure:
/// 22
/// / | \
/// 10 14 21
/// / / \ \ / \ / | \
/// 2 4 6 9 12 13 17 18 20
/// / \ | | / \ | / \ |
/// 0 1 3 5 7 8 11 15 16 19
///
/// Expected traversal order is indicated by each leaf's number
fn generate_tree() -> DarkTreeResult<(DarkTree<i32>, Vec<i32>)> {
let mut tree = DarkTree::new(
22,
vec![
DarkTree::new(
10,
vec![
DarkTree::new(
2,
vec![
DarkTree::new(0, vec![], None, None),
DarkTree::new(1, vec![], None, None),
],
None,
None,
),
DarkTree::new(4, vec![DarkTree::new(3, vec![], None, None)], None, None),
DarkTree::new(6, vec![DarkTree::new(5, vec![], None, None)], None, None),
DarkTree::new(
9,
vec![
DarkTree::new(7, vec![], None, None),
DarkTree::new(8, vec![], None, None),
],
None,
None,
),
],
None,
None,
),
DarkTree::new(
14,
vec![
DarkTree::new(12, vec![DarkTree::new(11, vec![], None, None)], None, None),
DarkTree::new(13, vec![], None, None),
],
None,
None,
),
DarkTree::new(
21,
vec![
DarkTree::new(
17,
vec![
DarkTree::new(15, vec![], None, None),
DarkTree::new(16, vec![], None, None),
],
None,
None,
),
DarkTree::new(18, vec![], None, None),
DarkTree::new(20, vec![DarkTree::new(19, vec![], None, None)], None, None),
],
None,
None,
),
],
None,
None,
);
tree.build()?;
let traversal_order = (0..23).collect();
Ok((tree, traversal_order))
}
#[test]
pub fn test_darktree_iterator() -> DarkTreeResult<()> {
let (tree, traversal_order) = generate_tree()?;
// Use [`DarkTree`] iterator to collect current
// data, in order
let nums: Vec<i32> = tree.iter().map(|x| x.info.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().info.data, traversal_order[1]);
// Thanks for reading
Ok(())
}
#[test]
fn test_darktree_traversal_order() -> DarkTreeResult<()> {
let (mut tree, traversal_order) = generate_tree()?;
// Loop using the fusion immutable iterator,
// verifying we grab the correct [`DarkTreeLeaf`]
// immutable reference, as per expected
// traversal order.
let mut index = 0;
for leaf in &tree {
assert_eq!(leaf.info.data, traversal_order[index]);
index += 1;
}
// Loop using the fusion mutable iterator,
// verifying we grab the correct [`DarkTreeLeaf`]
// mutable reference, as per expected traversal
// order.
index = 0;
for leaf in &mut tree {
assert_eq!(leaf.info.data, traversal_order[index]);
index += 1;
}
// Loop using [`DarkTree`] .iter_mut() mutable
// iterator, verifying we grab the correct [`DarkTreeLeaf`]
// mutable reference, as per expected traversal
// order.
for (index, leaf) in tree.iter_mut().enumerate() {
assert_eq!(leaf.info.data, traversal_order[index]);
}
// Loop using [`DarkTree`] .iter() immutable
// iterator, verifying we grab the correct [`DarkTreeLeaf`]
// immutable reference, as per expected traversal
// order.
for (index, leaf) in tree.iter().enumerate() {
assert_eq!(leaf.info.data, traversal_order[index]);
}
// Loop using [`DarkTree`] .into_iter() iterator,
// which consumes (moves) the tree, verifying we
// collect the correct [`DarkTreeLeaf`], as per expected
// traversal order.
for (index, leaf) in tree.into_iter().enumerate() {
assert_eq!(leaf.info.data, traversal_order[index]);
}
// Thanks for reading
Ok(())
}
#[test]
fn test_darktree_mut_iterator() -> DarkTreeResult<()> {
let (mut tree, _) = generate_tree()?;
// Loop using [`DarkTree`] .iter_mut() mutable
// iterator, grabing a mutable reference over a
// [`DarkTreeLeaf`], and mutating its inner data.
for leaf in tree.iter_mut() {
leaf.info.data += 1;
}
// Loop using the fusion mutable iterator,
// grabing a mutable reference over a
// [`DarkTreeLeaf`], and mutating its inner data.
for leaf in &mut tree {
leaf.info.data += 1;
}
// Verify performed mutation actually happened
// on original tree. Additionally we verify all
// indexes are the expected ones.
assert_eq!(
tree,
DarkTree {
leaf: DarkTreeLeaf {
index: 22,
info: DarkLeaf { data: 24, parent_index: None, children_indexes: vec![10, 14, 21] },
},
children: vec![
DarkTree {
leaf: DarkTreeLeaf {
index: 10,
info: DarkLeaf {
data: 12,
parent_index: Some(22),
children_indexes: vec![2, 4, 6, 9],
},
},
children: vec![
DarkTree {
leaf: DarkTreeLeaf {
index: 2,
info: DarkLeaf {
data: 4,
parent_index: Some(10),
children_indexes: vec![0, 1],
},
},
children: vec![
DarkTree {
leaf: DarkTreeLeaf {
index: 0,
info: DarkLeaf {
data: 2,
parent_index: Some(2),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 1,
info: DarkLeaf {
data: 3,
parent_index: Some(2),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 4,
info: DarkLeaf {
data: 6,
parent_index: Some(10),
children_indexes: vec![3],
},
},
children: vec![DarkTree {
leaf: DarkTreeLeaf {
index: 3,
info: DarkLeaf {
data: 5,
parent_index: Some(4),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 6,
info: DarkLeaf {
data: 8,
parent_index: Some(10),
children_indexes: vec![5],
},
},
children: vec![DarkTree {
leaf: DarkTreeLeaf {
index: 5,
info: DarkLeaf {
data: 7,
parent_index: Some(6),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 9,
info: DarkLeaf {
data: 11,
parent_index: Some(10),
children_indexes: vec![7, 8],
},
},
children: vec![
DarkTree {
leaf: DarkTreeLeaf {
index: 7,
info: DarkLeaf {
data: 9,
parent_index: Some(9),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 8,
info: DarkLeaf {
data: 10,
parent_index: Some(9),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
],
min_capacity: 1,
max_capacity: None,
},
],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 14,
info: DarkLeaf {
data: 16,
parent_index: Some(22),
children_indexes: vec![12, 13],
},
},
children: vec![
DarkTree {
leaf: DarkTreeLeaf {
index: 12,
info: DarkLeaf {
data: 14,
parent_index: Some(14),
children_indexes: vec![11],
},
},
children: vec![DarkTree {
leaf: DarkTreeLeaf {
index: 11,
info: DarkLeaf {
data: 13,
parent_index: Some(12),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 13,
info: DarkLeaf {
data: 15,
parent_index: Some(14),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 21,
info: DarkLeaf {
data: 23,
parent_index: Some(22),
children_indexes: vec![17, 18, 20],
},
},
children: vec![
DarkTree {
leaf: DarkTreeLeaf {
index: 17,
info: DarkLeaf {
data: 19,
parent_index: Some(21),
children_indexes: vec![15, 16],
},
},
children: vec![
DarkTree {
leaf: DarkTreeLeaf {
index: 15,
info: DarkLeaf {
data: 17,
parent_index: Some(17),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 16,
info: DarkLeaf {
data: 18,
parent_index: Some(17),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 18,
info: DarkLeaf {
data: 20,
parent_index: Some(21),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 20,
info: DarkLeaf {
data: 22,
parent_index: Some(21),
children_indexes: vec![19]
},
},
children: vec![DarkTree {
leaf: DarkTreeLeaf {
index: 19,
info: DarkLeaf {
data: 21,
parent_index: Some(20),
children_indexes: vec![],
},
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},],
min_capacity: 1,
max_capacity: None,
},
],
min_capacity: 1,
max_capacity: None,
},
],
min_capacity: 1,
max_capacity: None,
}
);
let traversal_order: Vec<i32> = (2..25).collect();
// Use [`DarkTree`] iterator to collect current
// data, in order
let nums: Vec<i32> = tree.iter().map(|x| x.info.data).collect();
// Verify iterator collected the data in the expected
// traversal order.
assert_eq!(nums, traversal_order);
// Thanks for reading
Ok(())
}
#[test]
pub fn test_darktree_min_capacity() -> DarkTreeResult<()> {
// Generate a new [`DarkTree`] with min capacity 0
let mut tree = DarkTree::new(0, vec![], Some(0), None);
// Verify that min capacity was properly setup to 1
assert_eq!(
tree,
DarkTree {
leaf: DarkTreeLeaf {
index: 0,
info: DarkLeaf { data: 0, parent_index: None, children_indexes: vec![] },
},
children: vec![],
min_capacity: 1,
max_capacity: None
}
);
// Verify that building it will succeed, as capacity
// would have ben setup to 1
assert!(tree.build().is_ok());
// Generate a new [`DarkTree`] manually with
// min capacity 0
let mut tree = DarkTree {
leaf: DarkTreeLeaf {
index: 0,
info: DarkLeaf { data: 0, parent_index: None, children_indexes: vec![] },
},
children: vec![],
min_capacity: 0,
max_capacity: None,
};
// Verify that building it will fail
assert!(tree.build().is_err());
// Thanks for reading
Ok(())
}
#[test]
pub fn test_darktree_max_capacity() -> DarkTreeResult<()> {
// Generate a new [`DarkTree`] with max capacity 2
let mut tree = DarkTree::new(0, vec![], None, Some(2));
// Append a new node
tree.append(DarkTree::new(1, vec![], None, None))?;
// Try to append a new node
assert!(tree.append(DarkTree::new(2, vec![], None, None)).is_err());
// Verify tree builds
tree.build()?;
// Generate a new [`DarkTree`] with max capacity 2
let mut new_tree = DarkTree::new(3, vec![], None, Some(2));
// Append the previous tree as a new node
new_tree.append(tree)?;
// Check that max capacity has been exceeded
assert!(new_tree.check_max_capacity().is_err());
// Generate a new [`DarkTree`] manually with
// max capacity 1
let mut tree = DarkTree {
leaf: DarkTreeLeaf {
index: 0,
info: DarkLeaf { data: 0, parent_index: None, children_indexes: vec![] },
},
children: vec![
DarkTree {
leaf: DarkTreeLeaf {
index: 0,
info: DarkLeaf { data: 0, parent_index: None, children_indexes: vec![] },
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 0,
info: DarkLeaf { data: 0, parent_index: None, children_indexes: vec![] },
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
DarkTree {
leaf: DarkTreeLeaf {
index: 0,
info: DarkLeaf { data: 0, parent_index: None, children_indexes: vec![0] },
},
children: vec![],
min_capacity: 1,
max_capacity: None,
},
],
min_capacity: 1,
max_capacity: Some(1),
};
// Verify that building it will fail
assert!(tree.build().is_err());
// Generate a new [`DarkTree`] with max capacity 0,
// which is less that current min capacity 1
let mut tree = DarkTree::new(0, vec![], None, Some(0));
// Verify that building it will fail
assert!(tree.build().is_err());
// Thanks for reading
Ok(())
}
#[test]
pub fn test_darktree_flattened_vec() -> DarkTreeResult<()> {
let (mut tree, traversal_order) = generate_tree()?;
// Build the flattened vector
let vec = tree.build_vec()?;
// Verify vector integrity
dark_leaf_vec_integrity_check(&vec, Some(23), Some(23))?;
// Verify vector integrity will fail using different bounds:
// 1. Leafs less that min capacity
assert!(dark_leaf_vec_integrity_check(&vec, Some(24), None).is_err());
// 2. Leafs more than max capacity
assert!(dark_leaf_vec_integrity_check(&vec, None, Some(22)).is_err());
// 3. Max capacity less than min capacity
assert!(dark_leaf_vec_integrity_check(&vec, Some(23), Some(22)).is_err());
// Loop the vector to verify it follows expected
// traversal order.
let mut index = 0;
for leaf in vec {
assert_eq!(leaf.data, traversal_order[index]);
index += 1;
}
// Verify the tree is still intact
let (new_tree, _) = generate_tree()?;
assert_eq!(tree, new_tree);
// Generate a new [`DarkLeaf`] vector manually,
// corresponding to a [`DarkTree`] with a 2 children,
// with erroneous indexes
let vec = vec![
DarkLeaf { data: 0, parent_index: Some(2), children_indexes: vec![] },
DarkLeaf { data: 0, parent_index: Some(2), children_indexes: vec![] },
DarkLeaf { data: 0, parent_index: None, children_indexes: vec![0, 2] },
];
// Verify vector integrity will fail
assert!(dark_leaf_vec_integrity_check(&vec, None, None).is_err());
// Thanks for reading
Ok(())
}