From 1b4fcf8baabb016480adadcb983ec2c90bdfe2c6 Mon Sep 17 00:00:00 2001 From: aggstam Date: Wed, 6 Dec 2023 22:06:14 +0200 Subject: [PATCH] script/research/dark-forest: appending functionality along with optional capacity added --- script/research/dark-forest/src/error.rs | 3 + script/research/dark-forest/src/lib.rs | 70 ++++++++++++-- script/research/dark-forest/src/tests.rs | 114 ++++++++++++++++++++--- 3 files changed, 168 insertions(+), 19 deletions(-) diff --git a/script/research/dark-forest/src/error.rs b/script/research/dark-forest/src/error.rs index 1152d587d..5ba5c325d 100644 --- a/script/research/dark-forest/src/error.rs +++ b/script/research/dark-forest/src/error.rs @@ -30,4 +30,7 @@ pub enum DarkTreeError { #[error("Invalid DarkLeaf children index found for leaf: {0}")] InvalidLeafChildrenIndexes(usize), + + #[error("DarkTree capacity have been exceeded")] + CapacityExceeded, } diff --git a/script/research/dark-forest/src/lib.rs b/script/research/dark-forest/src/lib.rs index 5d6721296..5b97a8a70 100644 --- a/script/research/dark-forest/src/lib.rs +++ b/script/research/dark-forest/src/lib.rs @@ -74,25 +74,75 @@ impl DarkLeaf { /// 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. +/// 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 { /// This tree's leaf information, along with its data leaf: DarkLeaf, /// Vector containing all tree's branches(children tree) children: Vec>, + /// 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_capacity() or + /// .integrity_check(). + capacity: Option, } impl DarkTree { /// Initialize a [`DarkTree`], using provided data to /// generate its root. - fn new(data: T, children: Vec>) -> DarkTree { + fn new(data: T, children: Vec>, capacity: Option) -> DarkTree { let leaf = DarkLeaf::new(data); - Self { leaf, children } + Self { leaf, children, 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() + } + + /// Return the count of all [`DarkTree`] leafs. + fn len(&self) -> usize { + self.iter().count() + } + + /// Check if configured capacity have been exceeded. + fn check_capacity(&self) -> DarkTreeResult<()> { + if let Some(capacity) = self.capacity { + if self.len() >= capacity { + return Err(DarkTreeError::CapacityExceeded) + } + } + + Ok(()) + } + + /// Append a new child node to the [`DarkTree`], + /// if 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) -> DarkTreeResult<()> { + // Check current capacity + self.check_capacity()?; + + // 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 + /// and trigger the setup of its children indexes. fn set_parent_children_indexes(&mut self, parent_index: Option) { // Set our leafs parent index self.leaf.set_parent_index(parent_index); @@ -123,7 +173,7 @@ impl DarkTree { } /// Verify [`DarkTree`]'s leaf parent and children indexes validity, - /// and trigger the check of its children indexes + /// and trigger the check of its children indexes. fn check_parent_children_indexes(&self, parent_index: Option) -> DarkTreeResult<()> { // Check our leafs parent index if self.leaf.parent_index != parent_index { @@ -147,18 +197,22 @@ impl DarkTree { } /// Verify current [`DarkTree`]'s leafs indexes validity, - /// based on DFS post-order traversal order. This call + /// based on DFS post-order traversal order. Additionally, + /// check that capacity has not been exceeded. This call /// assumes it was triggered for the root of the tree, /// which has no parent index. fn integrity_check(&self) -> DarkTreeResult<()> { - // First we check each leaf index + // Check current capacity + self.check_capacity()?; + + // Check each leaf index for (index, leaf) in self.iter().enumerate() { if index != leaf.index { return Err(DarkTreeError::InvalidLeafIndex(leaf.index, index)) } } - // Now we trigger recursion to check each nodes rest indexes + // Trigger recursion to check each nodes rest indexes self.check_parent_children_indexes(None) } diff --git a/script/research/dark-forest/src/tests.rs b/script/research/dark-forest/src/tests.rs index f35c78ac9..953c3c604 100644 --- a/script/research/dark-forest/src/tests.rs +++ b/script/research/dark-forest/src/tests.rs @@ -38,29 +38,47 @@ fn generate_tree() -> DarkTreeResult<(DarkTree, Vec)> { DarkTree::new( 10, vec![ - DarkTree::new(2, vec![DarkTree::new(0, vec![]), DarkTree::new(1, vec![])]), - DarkTree::new(4, vec![DarkTree::new(3, vec![])]), - DarkTree::new(6, vec![DarkTree::new(5, vec![])]), - DarkTree::new(9, vec![DarkTree::new(7, vec![]), DarkTree::new(8, vec![])]), + DarkTree::new( + 2, + vec![DarkTree::new(0, vec![], None), DarkTree::new(1, vec![], None)], + None, + ), + DarkTree::new(4, vec![DarkTree::new(3, vec![], None)], None), + DarkTree::new(6, vec![DarkTree::new(5, vec![], None)], None), + DarkTree::new( + 9, + vec![DarkTree::new(7, vec![], None), DarkTree::new(8, vec![], None)], + None, + ), ], + None, ), DarkTree::new( 14, - vec![DarkTree::new(12, vec![DarkTree::new(11, vec![])]), DarkTree::new(13, vec![])], + vec![ + DarkTree::new(12, vec![DarkTree::new(11, vec![], None)], None), + DarkTree::new(13, vec![], None), + ], + None, ), DarkTree::new( 21, vec![ - DarkTree::new(17, vec![DarkTree::new(15, vec![]), DarkTree::new(16, vec![])]), - DarkTree::new(18, vec![]), - DarkTree::new(20, vec![DarkTree::new(19, vec![])]), + DarkTree::new( + 17, + vec![DarkTree::new(15, vec![], None), DarkTree::new(16, vec![], None)], + None, + ), + DarkTree::new(18, vec![], None), + DarkTree::new(20, vec![DarkTree::new(19, vec![], None)], None), ], + None, ), ], + None, ); - tree.index(); - tree.integrity_check()?; + tree.build()?; let traversal_order = (0..23).collect(); @@ -195,6 +213,7 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -204,8 +223,10 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, }, - ] + ], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -222,7 +243,9 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, },], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -239,7 +262,9 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, },], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -257,6 +282,7 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -266,10 +292,13 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, }, ], + capacity: None, }, ], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -294,7 +323,9 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, },], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -304,8 +335,10 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, }, ], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -331,6 +364,7 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -340,8 +374,10 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, }, ], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -351,6 +387,7 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, }, DarkTree { leaf: DarkLeaf { @@ -367,11 +404,15 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { children_indexes: vec![] }, children: vec![], + capacity: None, },], + capacity: None, }, ], + capacity: None, }, ], + capacity: None, } ); @@ -388,3 +429,54 @@ fn test_darktree_mut_iterator() -> DarkTreeResult<()> { // Thanks for reading Ok(()) } + +#[test] +pub fn test_darktree_max_capacity() -> DarkTreeResult<()> { + // Generate a new [`DarkTree`] with capacity 2 + let mut tree = DarkTree::new(0, vec![], Some(2)); + + // Append a new node + tree.append(DarkTree::new(1, vec![], None))?; + + // Try to append a new node + assert!(tree.append(DarkTree::new(2, vec![], None)).is_err()); + + // Generate a new [`DarkTree`] with capacity 2 + let mut new_tree = DarkTree::new(3, vec![], Some(2)); + + // Append the previous tree as a new node + new_tree.append(tree)?; + + // Check that capacity has been exceeded + assert!(new_tree.check_capacity().is_err()); + + // Generate a new [`DarkTree`] manually with + // capacity 1 + let mut tree = DarkTree { + leaf: DarkLeaf { data: 0, index: 0, parent_index: None, children_indexes: vec![] }, + children: vec![ + DarkTree { + leaf: DarkLeaf { data: 0, index: 0, parent_index: None, children_indexes: vec![] }, + children: vec![], + capacity: None, + }, + DarkTree { + leaf: DarkLeaf { data: 0, index: 0, parent_index: None, children_indexes: vec![] }, + children: vec![], + capacity: None, + }, + DarkTree { + leaf: DarkLeaf { data: 0, index: 0, parent_index: None, children_indexes: vec![0] }, + children: vec![], + capacity: None, + }, + ], + capacity: Some(1), + }; + + // Verify that building it will fail + assert!(tree.build().is_err()); + + // Thanks for reading + Ok(()) +}