From 9296b41487a06ddc98bc16aae2225e61b976e8b4 Mon Sep 17 00:00:00 2001 From: azizkayumov Date: Fri, 29 Sep 2023 23:00:31 +0900 Subject: [PATCH 01/12] path trait --- src/lib.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/node.rs | 30 +++------------- src/path.rs | 43 +++++++++++++++++++++++ 3 files changed, 145 insertions(+), 27 deletions(-) create mode 100644 src/path.rs diff --git a/src/lib.rs b/src/lib.rs index 9aba896..19eb7aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,104 @@ -#![allow(dead_code, clippy::module_name_repetitions)] // yes, I want to name my structs with the same name as the file +#![allow(dead_code, clippy::module_name_repetitions)] +use node::Node; // yes, I want to name my structs with the same name as the file mod access; mod connected; mod cut; mod link; mod node; +mod path; mod splay; + +pub struct LinkCutTree { + forest: Vec, +} + +impl LinkCutTree { + #[must_use] + pub fn new(n: usize) -> Self { + let nodes = (0..n).map(|i| Node::new(i, 0.0)).collect(); + Self { forest: nodes } + } + + pub fn link(&mut self, child: usize, parent: usize) { + link::link(&mut self.forest, child, parent); + } + + pub fn cut(&mut self, child: usize) { + cut::cut(&mut self.forest, child); + } + + pub fn connected(&mut self, a: usize, b: usize) -> bool { + connected::connected(&mut self.forest, a, b) + } +} + +#[cfg(test)] +mod tests { + use crate::node::Node; + + fn create_nodes(n: usize) -> Vec { + (0..n).map(|i| Node::new(i, 0.0)).collect() + } + + #[test] + pub fn rooted_tree() { + // We form a tree with the following structure: + // 0 + // / \ + // 1 6 + // / \ \ + // 2 3 7 + // / \ \ + // 4 5 8 + // / + // 9 + let mut lctree = super::LinkCutTree::new(10); + lctree.link(1, 0); + lctree.link(2, 1); + lctree.link(3, 1); + lctree.link(4, 3); + lctree.link(5, 3); + lctree.link(6, 0); + lctree.link(7, 6); + lctree.link(8, 7); + lctree.link(9, 8); + + // checking connectivity: + for i in 0..10 { + for j in 0..10 { + assert!(lctree.connected(i, j)); + } + } + + // we cut node 6 from its parent 0: + lctree.cut(6); + + // the forest should now look like this: + // 0 + // / + // 1 6 + // / \ \ + // 2 3 7 + // / \ \ + // 4 5 8 + // / + // 9 + + // we check connectivity again for the two trees: + for i in 0..6 { + for j in 0..6 { + assert!(lctree.connected(i, j)); + } + } + for i in 6..10 { + for j in 6..10 { + assert!(lctree.connected(i, j)); + } + } + for i in 0..6 { + for j in 6..10 { + assert!(!lctree.connected(i, j)); + } + } + } +} diff --git a/src/node.rs b/src/node.rs index 889a62f..af67a16 100644 --- a/src/node.rs +++ b/src/node.rs @@ -5,46 +5,24 @@ pub enum Parent { Root, // root of the tree } -pub struct Path { - pub max_weight_idx: usize, - pub max_weight: f64, -} - -impl Path { - pub fn new(max_weight_idx: usize, max_weight: f64) -> Self { - Path { - max_weight_idx, - max_weight, - } - } - - pub fn update(&mut self, idx: usize, weight: f64) { - if weight > self.max_weight { - self.max_weight = weight; - self.max_weight_idx = idx; - } - } -} - pub struct Node { + pub idx: usize, pub left: Option, pub right: Option, pub parent: Parent, pub flipped: bool, - // path aggregates - pub weight: f64, - pub path: Path, + pub weight: f64, // for path aggregation } impl Node { pub fn new(idx: usize, weight: f64) -> Self { Node { + idx, left: None, right: None, parent: Parent::Root, flipped: false, - weight: 0.0, - path: Path::new(idx, weight), + weight, } } } diff --git a/src/path.rs b/src/path.rs new file mode 100644 index 0000000..fd19a61 --- /dev/null +++ b/src/path.rs @@ -0,0 +1,43 @@ +use crate::node::Node; + +pub trait Path { + fn aggregate(&mut self, other: &Node); +} + +pub struct FindMax { + pub max_weight_idx: usize, + pub max_weight: f64, +} + +impl Path for FindMax { + fn aggregate(&mut self, other: &Node) { + if other.weight > self.max_weight { + self.max_weight = other.weight; + self.max_weight_idx = other.idx; + } + } +} + +pub struct FindMin { + pub min_weight_idx: usize, + pub min_weight: f64, +} + +impl Path for FindMin { + fn aggregate(&mut self, other: &Node) { + if other.weight < self.min_weight { + self.min_weight = other.weight; + self.min_weight_idx = other.idx; + } + } +} + +pub struct FindSum { + pub sum: f64, +} + +impl Path for FindSum { + fn aggregate(&mut self, other: &Node) { + self.sum += other.weight; + } +} From c10b644c529bf6caf5a1eff8c04cede533669719 Mon Sep 17 00:00:00 2001 From: azizkayumov Date: Sun, 1 Oct 2023 00:20:27 +0900 Subject: [PATCH 02/12] clean up --- src/access.rs | 71 ----------- src/connected.rs | 72 ----------- src/cut.rs | 96 -------------- src/lib.rs | 316 ++++++++++++++++++++++++++++++++++++++++++++--- src/link.rs | 137 -------------------- src/splay.rs | 10 +- 6 files changed, 304 insertions(+), 398 deletions(-) delete mode 100644 src/access.rs delete mode 100644 src/connected.rs delete mode 100644 src/cut.rs delete mode 100644 src/link.rs diff --git a/src/access.rs b/src/access.rs deleted file mode 100644 index 5575e41..0000000 --- a/src/access.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::{ - node::{Node, Parent}, - splay::splay, -}; - -// constructs a path from a node to the root of the tree -pub fn access(forest: &mut Vec, node_idx: usize) { - assert!(node_idx < forest.len(), "access: node_idx out of bounds"); - splay(forest, node_idx); - - if let Some(right_idx) = forest[node_idx].right { - forest[node_idx].right = None; - forest[right_idx].parent = Parent::Path(node_idx); - } - - while let Parent::Path(path_idx) = forest[node_idx].parent { - splay(forest, path_idx); - // detach the right child of the path parent - if let Some(right_idx) = forest[path_idx].right { - forest[right_idx].parent = Parent::Path(path_idx); - forest[path_idx].right = None; - } - - // attach the node as the path parent's right child - forest[path_idx].right = Some(node_idx); - forest[node_idx].parent = Parent::Node(path_idx); - splay(forest, node_idx); - } -} - -#[cfg(test)] -mod tests { - use crate::node::{Node, Parent}; - - fn create_nodes(n: usize) -> Vec { - (0..n).map(|i| Node::new(i, 0.0)).collect() - } - - #[test] - pub fn base_case() { - // access a single node, should do nothing - let mut forest = create_nodes(1); - super::access(&mut forest, 0); - assert!(matches!(forest[0].parent, Parent::Root)); - } - - #[test] - pub fn access_leaf() { - let mut forest = create_nodes(3); - // '1' has a path pointer to '0', '1' has a right child '2'. - // after access(2), '2' should be the root of the tree: - // 0 0 0 2 - // | | \ / - // 1 => 2 => 2 => 0 - // \ / / \ - // 2 1 1 1 - forest[1].parent = Parent::Path(0); - forest[1].right = Some(2); - forest[2].parent = Parent::Node(1); - super::access(&mut forest, 2); - assert!(matches!(forest[2].parent, Parent::Root)); - assert_eq!(forest[2].right, None); - assert_eq!(forest[2].left, Some(0)); - assert!(matches!(forest[0].parent, Parent::Node(2))); - assert_eq!(forest[0].left, None); - assert_eq!(forest[0].right, Some(1)); - assert!(matches!(forest[1].parent, Parent::Node(0))); - assert_eq!(forest[1].left, None); - assert_eq!(forest[1].right, None); - } -} diff --git a/src/connected.rs b/src/connected.rs deleted file mode 100644 index 075f387..0000000 --- a/src/connected.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::{ - access::access, - node::{Node, Parent}, -}; - -// checks if v and w are connected in the forest -pub fn connected(forest: &mut Vec, v: usize, w: usize) -> bool { - assert!( - v < forest.len() || w < forest.len(), - "connected: node_idx out of bounds" - ); - access(forest, v); // v is now the root of the tree - access(forest, w); - // if access(w) messed with the root of the tree, then v and w are connected: - !matches!(forest[v].parent, Parent::Root) || v == w -} - -#[cfg(test)] -mod tests { - use crate::node::{Node, Parent}; - - fn create_nodes(n: usize) -> Vec { - (0..n).map(|i| Node::new(i, 0.0)).collect() - } - - #[test] - pub fn base_case() { - // check two nodes, should return false - let mut forest = create_nodes(2); - assert!(!super::connected(&mut forest, 0, 1)); - } - - #[test] - pub fn connected_with_root() { - // check three nodes, one is root: - // 0 - // / \ - // 1 2 - let mut forest = create_nodes(3); - forest[0].left = Some(1); - forest[0].right = Some(2); - forest[1].parent = Parent::Node(0); - forest[2].parent = Parent::Node(0); - - assert!(super::connected(&mut forest, 0, 1)); - assert!(super::connected(&mut forest, 0, 2)); - assert!(super::connected(&mut forest, 1, 2)); - assert!(super::connected(&mut forest, 0, 0)); - assert!(super::connected(&mut forest, 1, 1)); - assert!(super::connected(&mut forest, 2, 2)); - } - - #[test] - pub fn connected_with_path_pointers() { - // check two trees that are connected by a path pointer - // 0 - // / \ - // 1 2 - // | - // 3 - let mut forest = create_nodes(4); - forest[0].left = Some(1); - forest[0].right = Some(2); - forest[1].parent = Parent::Node(0); - forest[2].parent = Parent::Node(0); - forest[3].parent = Parent::Path(2); - - assert!(super::connected(&mut forest, 0, 3)); - assert!(super::connected(&mut forest, 1, 3)); - assert!(super::connected(&mut forest, 2, 3)); - } -} diff --git a/src/cut.rs b/src/cut.rs deleted file mode 100644 index 61260ec..0000000 --- a/src/cut.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::{ - access::access, - node::{Node, Parent}, -}; - -// cuts the link between v and its parent. -pub fn cut(forest: &mut Vec, v: usize) { - assert!(v < forest.len(), "cut: node_idx out of bounds"); - self::access(forest, v); - if let Some(left) = forest[v].left { - forest[left].parent = Parent::Root; - forest[v].left = None; - } -} - -#[cfg(test)] -mod tests { - use crate::{ - connected::connected, - cut::cut, - link::link, - node::{Node, Parent}, - }; - - fn create_nodes(n: usize) -> Vec { - (0..n).map(|i| Node::new(i, 0.0)).collect() - } - - #[test] - pub fn base_case() { - let mut forest = create_nodes(2); - assert!(!connected(&mut forest, 0, 1)); // not connected yet - - link(&mut forest, 0, 1); - assert!(matches!(forest[1].parent, Parent::Root)); - assert_eq!(forest[1].right, Some(0)); - assert_eq!(forest[1].left, None); - assert!(matches!(forest[0].parent, Parent::Node(1))); - // 1 - // \ <= link(0, 1) - // 0 - - assert!(connected(&mut forest, 0, 1)); // now connected - assert!(matches!(forest[1].parent, Parent::Root)); - assert_eq!(forest[1].right, None); - assert_eq!(forest[1].left, None); - assert!(matches!(forest[0].parent, Parent::Path(1))); - // 0 1 - // / => | - // 1 0 - - super::cut(&mut forest, 0); - assert!(matches!(forest[0].parent, Parent::Root)); - assert_eq!(forest[0].right, None); - // 0 0 - // / => - // 1 1 - assert!(!connected(&mut forest, 0, 1)); // now disconnected - } - - #[test] - pub fn cut_into_two_subtrees() { - let mut forest = create_nodes(5); - forest[0].left = Some(1); - forest[1].parent = Parent::Node(0); - forest[1].left = Some(2); - forest[2].parent = Parent::Node(1); - forest[3].right = Some(4); - forest[4].parent = Parent::Node(3); - // Given two trees: - // 0 3 - // / \ - // 1 4 - // / - // 2 - link(&mut forest, 2, 3); - // link(2, 3) should result in: - // 3 - // | \ - // 4 2 - // | - // 1 - // \ - // 0 - assert!(matches!(forest[3].parent, Parent::Root)); - assert!(matches!(forest[4].parent, Parent::Path(3))); - assert_eq!(forest[3].right, Some(2)); - assert!(matches!(forest[2].parent, Parent::Node(3))); - assert!(matches!(forest[1].parent, Parent::Path(2))); - assert_eq!(forest[1].right, Some(0)); - assert!(connected(&mut forest, 2, 3)); - cut(&mut forest, 2); - assert!(!connected(&mut forest, 2, 3)); - assert!(!connected(&mut forest, 2, 4)); - } -} diff --git a/src/lib.rs b/src/lib.rs index 19eb7aa..3b5b764 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,11 @@ -#![allow(dead_code, clippy::module_name_repetitions)] -use node::Node; // yes, I want to name my structs with the same name as the file -mod access; -mod connected; -mod cut; -mod link; +#![allow(dead_code, clippy::module_name_repetitions)] // yes, I want to name my structs with the same name as the file mod node; mod path; mod splay; +use crate::{ + node::{Node, Parent}, + splay::splay, +}; pub struct LinkCutTree { forest: Vec, @@ -19,30 +18,313 @@ impl LinkCutTree { Self { forest: nodes } } - pub fn link(&mut self, child: usize, parent: usize) { - link::link(&mut self.forest, child, parent); + // Constructs a path from a node to the root of the tree. + pub fn access(&mut self, v: usize) { + splay(&mut self.forest, v); + + if let Some(right_idx) = self.forest[v].right { + self.forest[v].right = None; + self.forest[right_idx].parent = Parent::Path(v); + } + + while let Parent::Path(path_idx) = self.forest[v].parent { + splay(&mut self.forest, path_idx); + // detach the right child of the path parent + if let Some(right_idx) = self.forest[path_idx].right { + self.forest[right_idx].parent = Parent::Path(path_idx); + self.forest[path_idx].right = None; + } + + // attach the node as the path parent's right child + self.forest[path_idx].right = Some(v); + self.forest[v].parent = Parent::Node(path_idx); + splay(&mut self.forest, v); + } } - pub fn cut(&mut self, child: usize) { - cut::cut(&mut self.forest, child); + // Creates a link between two nodes in the forest (w becomes the parent of v) + pub fn link(&mut self, v: usize, w: usize) { + self.access(v); + self.access(w); + if !matches!(self.forest[v].parent, Parent::Root) || v == w { + return; // already connected + } + self.forest[w].right = Some(v); + self.forest[v].parent = Parent::Node(w); + } + + // Checks if v and w are connected in the forest + pub fn connected(&mut self, v: usize, w: usize) -> bool { + self.access(v); // v is now the root of the tree + self.access(w); + // if access(w) messed with the root of the tree, then v and w are connected: + !matches!(self.forest[v].parent, Parent::Root) || v == w } - pub fn connected(&mut self, a: usize, b: usize) -> bool { - connected::connected(&mut self.forest, a, b) + // Cuts the link between v and its parent. + pub fn cut(&mut self, v: usize) { + self.access(v); + if let Some(left) = self.forest[v].left { + self.forest[left].parent = Parent::Root; + self.forest[v].left = None; + } } } #[cfg(test)] mod tests { - use crate::node::Node; + use crate::node::Parent; + + #[test] + pub fn access_base_case() { + // access a single node, should do nothing + let mut tree = super::LinkCutTree::new(1); + tree.access(0); + assert!(matches!(tree.forest[0].parent, Parent::Root)); + } + + #[test] + pub fn access_leaf() { + let mut tree = super::LinkCutTree::new(3); + // '1' has a path pointer to '0', '1' has a right child '2'. + // after access(2), '2' should be the root of the tree: + // 0 0 0 2 + // | | \ / + // 1 => 2 => 2 => 0 + // \ / / \ + // 2 1 1 1 + tree.forest[1].parent = Parent::Path(0); + tree.forest[1].right = Some(2); + tree.forest[2].parent = Parent::Node(1); + tree.access(2); + assert!(matches!(tree.forest[2].parent, Parent::Root)); + assert_eq!(tree.forest[2].right, None); + assert_eq!(tree.forest[2].left, Some(0)); + assert!(matches!(tree.forest[0].parent, Parent::Node(2))); + assert_eq!(tree.forest[0].left, None); + assert_eq!(tree.forest[0].right, Some(1)); + assert!(matches!(tree.forest[1].parent, Parent::Node(0))); + assert_eq!(tree.forest[1].left, None); + assert_eq!(tree.forest[1].right, None); + } + + #[test] + pub fn link_base_case() { + let mut tree = super::LinkCutTree::new(2); + assert!(!tree.connected(0, 1)); // not connected yet + tree.link(0, 1); + assert!(tree.connected(0, 1)); // now connected + } + + #[test] + pub fn link_already_connected() { + // '2' has a right child '3': + // link(0, 3) should add no link, and result in (| denotes a path pointer): + // 0 0 0 3 + // / \ / | / | / + // 1 2 => 1 2 => 1 3 => 0 + // \ \ / / \ + // 3 3 2 1 2 + // + let mut tree = super::LinkCutTree::new(4); + tree.forest[0].left = Some(1); + tree.forest[0].right = Some(2); + tree.forest[1].parent = Parent::Node(0); + tree.forest[2].parent = Parent::Node(0); + tree.forest[2].right = Some(3); + tree.forest[3].parent = Parent::Node(2); + tree.link(0, 3); + assert!(matches!(tree.forest[3].parent, Parent::Root)); + assert_eq!(tree.forest[3].left, Some(0)); + assert_eq!(tree.forest[3].right, None); + assert!(matches!(tree.forest[0].parent, Parent::Node(3))); + assert_eq!(tree.forest[0].left, Some(1)); + assert_eq!(tree.forest[0].right, Some(2)); + assert!(matches!(tree.forest[1].parent, Parent::Node(0))); + assert_eq!(tree.forest[1].left, None); + assert_eq!(tree.forest[1].right, None); + assert!(matches!(tree.forest[2].parent, Parent::Node(0))); + assert_eq!(tree.forest[2].left, None); + assert_eq!(tree.forest[2].right, None); + } + + #[test] + pub fn link_already_connected_with_path() { + // '3' has a path pointer to '2', and '2' has a path pointer to '0': + // link(0, 1) should add no link, and result in (| denotes a path pointer): + // 0 0 0 3 + // / \ / | / | / + // 1 2 => 1 2 => 1 3 => 0 + // | | / / \ + // 3 3 2 1 2 + // + let mut tree = super::LinkCutTree::new(4); + tree.forest[0].left = Some(1); + tree.forest[0].right = Some(2); + tree.forest[1].parent = Parent::Node(0); + tree.forest[2].parent = Parent::Node(0); + tree.forest[3].parent = Parent::Path(2); + tree.link(0, 3); + assert!(matches!(tree.forest[3].parent, Parent::Root)); + assert_eq!(tree.forest[3].left, Some(0)); + assert_eq!(tree.forest[3].right, None); + assert!(matches!(tree.forest[0].parent, Parent::Node(3))); + assert_eq!(tree.forest[0].left, Some(1)); + assert_eq!(tree.forest[0].right, Some(2)); + assert!(matches!(tree.forest[1].parent, Parent::Node(0))); + assert_eq!(tree.forest[1].left, None); + assert_eq!(tree.forest[1].right, None); + assert!(matches!(tree.forest[2].parent, Parent::Node(0))); + assert_eq!(tree.forest[2].left, None); + assert_eq!(tree.forest[2].right, None); + } + + #[test] + pub fn link_to_leftmost() { + // Given two trees: + // 0 3 + // / \ + // 1 2 + // link(1, 3) should result in a single tree (| denotes a path pointer): + // 1 3 1 + // | | \ + // 0 => 0 3 + // \ \ + // 2 2 + // + let mut tree = super::LinkCutTree::new(4); + tree.forest[0].left = Some(1); + tree.forest[0].right = Some(2); + tree.forest[1].parent = Parent::Node(0); + tree.forest[2].parent = Parent::Node(0); + tree.link(3, 1); + assert!(matches!(tree.forest[1].parent, Parent::Root)); + assert_eq!(tree.forest[1].right, Some(3)); + assert_eq!(tree.forest[1].left, None); + assert!(matches!(tree.forest[3].parent, Parent::Node(1))); + assert_eq!(tree.forest[3].right, None); + assert_eq!(tree.forest[3].left, None); + assert!(matches!(tree.forest[0].parent, Parent::Path(1))); + assert_eq!(tree.forest[0].right, Some(2)); + assert_eq!(tree.forest[0].left, None); + assert!(matches!(tree.forest[2].parent, Parent::Node(0))); + assert_eq!(tree.forest[2].right, None); + assert_eq!(tree.forest[2].left, None); + } + + #[test] + pub fn connected_with_root() { + // check three nodes, one is root: + // 0 + // / \ + // 1 2 + let mut tree = super::LinkCutTree::new(3); + tree.forest[0].left = Some(1); + tree.forest[0].right = Some(2); + tree.forest[1].parent = Parent::Node(0); + tree.forest[2].parent = Parent::Node(0); + + assert!(tree.connected(0, 1)); + assert!(tree.connected(0, 2)); + assert!(tree.connected(1, 2)); + assert!(tree.connected(0, 0)); + assert!(tree.connected(1, 1)); + assert!(tree.connected(2, 2)); + } + + #[test] + pub fn connected_with_path_pointers() { + // check two trees that are connected by a path pointer + // 0 + // / \ + // 1 2 + // | + // 3 + let mut tree = super::LinkCutTree::new(4); + tree.forest[0].left = Some(1); + tree.forest[0].right = Some(2); + tree.forest[1].parent = Parent::Node(0); + tree.forest[2].parent = Parent::Node(0); + tree.forest[3].parent = Parent::Path(2); + + assert!(tree.connected(0, 3)); + assert!(tree.connected(1, 3)); + assert!(tree.connected(2, 3)); + } + + #[test] + pub fn cut_base_case() { + let mut tree = super::LinkCutTree::new(2); + assert!(!tree.connected(0, 1)); // not connected yet + + tree.link(0, 1); + assert!(matches!(tree.forest[1].parent, Parent::Root)); + assert_eq!(tree.forest[1].right, Some(0)); + assert_eq!(tree.forest[1].left, None); + assert!(matches!(tree.forest[0].parent, Parent::Node(1))); + // 1 + // \ <= link(0, 1) + // 0 + + assert!(tree.connected(0, 1)); // now connected + assert!(matches!(tree.forest[1].parent, Parent::Root)); + assert_eq!(tree.forest[1].right, None); + assert_eq!(tree.forest[1].left, None); + assert!(matches!(tree.forest[0].parent, Parent::Path(1))); + // 0 1 + // / => | + // 1 0 + + tree.cut(0); + assert!(matches!(tree.forest[0].parent, Parent::Root)); + assert_eq!(tree.forest[0].right, None); + // 0 0 + // / => + // 1 1 + assert!(!tree.connected(0, 1)); // now disconnected + } + + #[test] + pub fn cut_into_two_subtrees() { + let mut tree = super::LinkCutTree::new(5); + tree.forest[0].left = Some(1); + tree.forest[1].parent = Parent::Node(0); + tree.forest[1].left = Some(2); + tree.forest[2].parent = Parent::Node(1); + tree.forest[3].right = Some(4); + tree.forest[4].parent = Parent::Node(3); + // Given two trees: + // 0 3 + // / \ + // 1 4 + // / + // 2 + tree.link(2, 3); + // link(2, 3) should result in: + // 3 + // | \ + // 4 2 + // | + // 1 + // \ + // 0 + assert!(matches!(tree.forest[3].parent, Parent::Root)); + assert!(matches!(tree.forest[4].parent, Parent::Path(3))); + assert_eq!(tree.forest[3].right, Some(2)); + assert!(matches!(tree.forest[2].parent, Parent::Node(3))); + assert!(matches!(tree.forest[1].parent, Parent::Path(2))); + assert_eq!(tree.forest[1].right, Some(0)); + assert!(tree.connected(2, 3)); - fn create_nodes(n: usize) -> Vec { - (0..n).map(|i| Node::new(i, 0.0)).collect() + // we cut node 2 from its parent 3: + tree.cut(2); + assert!(!tree.connected(2, 3)); + assert!(!tree.connected(2, 4)); } #[test] - pub fn rooted_tree() { - // We form a tree with the following structure: + pub fn connectivity() { + // We form a link-cut tree from a rooted tree with the following structure: // 0 // / \ // 1 6 diff --git a/src/link.rs b/src/link.rs deleted file mode 100644 index 27e6377..0000000 --- a/src/link.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::{ - access::access, - node::{Node, Parent}, -}; - -// creates a link between two nodes in the forest (w becomes the parent of v) -pub fn link(forest: &mut Vec, v: usize, w: usize) { - assert!( - v < forest.len() && w < forest.len(), - "link: node_idx out of bounds" - ); - access(forest, v); - access(forest, w); - if !matches!(forest[v].parent, Parent::Root) || v == w { - return; // already connected - } - assert!(forest[w].right.is_none(), "link: w should be a root!"); - forest[w].right = Some(v); - forest[v].parent = Parent::Node(w); -} - -#[cfg(test)] -mod tests { - use super::link; - use crate::{ - connected::connected, - node::{Node, Parent}, - }; - - fn create_nodes(n: usize) -> Vec { - (0..n).map(|i| Node::new(i, 0.0)).collect() - } - - #[test] - pub fn base_case() { - let mut forest = create_nodes(2); - assert!(!connected(&mut forest, 0, 1)); // not connected yet - super::link(&mut forest, 0, 1); - assert!(connected(&mut forest, 0, 1)); // now connected - } - - #[test] - pub fn already_connected() { - // '2' has a right child '3': - // link(0, 3) should add no link, and result in (| denotes a path pointer): - // 0 0 0 3 - // / \ / | / | / - // 1 2 => 1 2 => 1 3 => 0 - // \ \ / / \ - // 3 3 2 1 2 - // - let mut forest = create_nodes(4); - forest[0].left = Some(1); - forest[0].right = Some(2); - forest[1].parent = Parent::Node(0); - forest[2].parent = Parent::Node(0); - forest[2].right = Some(3); - forest[3].parent = Parent::Node(2); - link(&mut forest, 0, 3); - assert!(matches!(forest[3].parent, Parent::Root)); - assert_eq!(forest[3].left, Some(0)); - assert_eq!(forest[3].right, None); - assert!(matches!(forest[0].parent, Parent::Node(3))); - assert_eq!(forest[0].left, Some(1)); - assert_eq!(forest[0].right, Some(2)); - assert!(matches!(forest[1].parent, Parent::Node(0))); - assert_eq!(forest[1].left, None); - assert_eq!(forest[1].right, None); - assert!(matches!(forest[2].parent, Parent::Node(0))); - assert_eq!(forest[2].left, None); - assert_eq!(forest[2].right, None); - } - - #[test] - pub fn already_connected_with_path() { - // '3' has a path pointer to '2', and '2' has a path pointer to '0': - // link(0, 1) should add no link, and result in (| denotes a path pointer): - // 0 0 0 3 - // / \ / | / | / - // 1 2 => 1 2 => 1 3 => 0 - // | | / / \ - // 3 3 2 1 2 - // - let mut forest = create_nodes(4); - forest[0].left = Some(1); - forest[0].right = Some(2); - forest[1].parent = Parent::Node(0); - forest[2].parent = Parent::Node(0); - forest[3].parent = Parent::Path(2); - link(&mut forest, 0, 3); - assert!(matches!(forest[3].parent, Parent::Root)); - assert_eq!(forest[3].left, Some(0)); - assert_eq!(forest[3].right, None); - assert!(matches!(forest[0].parent, Parent::Node(3))); - assert_eq!(forest[0].left, Some(1)); - assert_eq!(forest[0].right, Some(2)); - assert!(matches!(forest[1].parent, Parent::Node(0))); - assert_eq!(forest[1].left, None); - assert_eq!(forest[1].right, None); - assert!(matches!(forest[2].parent, Parent::Node(0))); - assert_eq!(forest[2].left, None); - assert_eq!(forest[2].right, None); - } - - #[test] - pub fn link_to_leftmost() { - // Given two trees: - // 0 3 - // / \ - // 1 2 - // link(1, 3) should result in a single tree (| denotes a path pointer): - // 1 3 1 - // | | \ - // 0 => 0 3 - // \ \ - // 2 2 - // - let mut forest = create_nodes(4); - forest[0].left = Some(1); - forest[0].right = Some(2); - forest[1].parent = Parent::Node(0); - forest[2].parent = Parent::Node(0); - link(&mut forest, 3, 1); - assert!(matches!(forest[1].parent, Parent::Root)); - assert_eq!(forest[1].right, Some(3)); - assert_eq!(forest[1].left, None); - assert!(matches!(forest[3].parent, Parent::Node(1))); - assert_eq!(forest[3].right, None); - assert_eq!(forest[3].left, None); - assert!(matches!(forest[0].parent, Parent::Path(1))); - assert_eq!(forest[0].right, Some(2)); - assert_eq!(forest[0].left, None); - assert!(matches!(forest[2].parent, Parent::Node(0))); - assert_eq!(forest[2].right, None); - assert_eq!(forest[2].left, None); - } -} diff --git a/src/splay.rs b/src/splay.rs index 5225824..ab0fcb8 100644 --- a/src/splay.rs +++ b/src/splay.rs @@ -1,10 +1,10 @@ use crate::node::{Node, Parent}; -fn rotate_left(forest: &mut Vec, node_idx: usize) { +fn rotate_left(forest: &mut [Node], node_idx: usize) { // base cases: // - node_idx is out of bounds // - node_idx should have a right child - if node_idx >= forest.len() || forest[node_idx].right.is_none() { + if forest[node_idx].right.is_none() { return; } @@ -27,11 +27,11 @@ fn rotate_left(forest: &mut Vec, node_idx: usize) { } } -fn rotate_right(forest: &mut Vec, node_idx: usize) { +fn rotate_right(forest: &mut [Node], node_idx: usize) { // base cases: // - node_idx is out of bounds // - node_idx should have a left child - if node_idx >= forest.len() || forest[node_idx].left.is_none() { + if forest[node_idx].left.is_none() { return; } @@ -55,7 +55,7 @@ fn rotate_right(forest: &mut Vec, node_idx: usize) { } // makes node_idx the root of its Splay tree -pub fn splay(forest: &mut Vec, node_idx: usize) { +pub fn splay(forest: &mut [Node], node_idx: usize) { assert!(node_idx < forest.len(), "splay: node_idx out of bounds"); while let Parent::Node(parent_idx) = forest[node_idx].parent { if forest[parent_idx].left == Some(node_idx) { From ea8b262269a8083e35f693be907a4867a28cfc2e Mon Sep 17 00:00:00 2001 From: azizkayumov Date: Sun, 1 Oct 2023 19:47:30 +0900 Subject: [PATCH 03/12] unflip on splay --- src/lib.rs | 4 ++++ src/splay.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 3b5b764..2cabeab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ mod node; mod path; mod splay; + use crate::{ node::{Node, Parent}, splay::splay, @@ -383,4 +384,7 @@ mod tests { } } } + + #[test] + pub fn findmin() {} } diff --git a/src/splay.rs b/src/splay.rs index ab0fcb8..8ca67fa 100644 --- a/src/splay.rs +++ b/src/splay.rs @@ -54,10 +54,30 @@ fn rotate_right(forest: &mut [Node], node_idx: usize) { } } +fn unflip(forest: &mut [Node], node_idx: usize) { + if forest[node_idx].flipped { + forest[node_idx].flipped = false; + std::mem::swap(&mut forest[node_idx].left, &mut forest[node_idx].right); + if let Some(left_child) = forest[node_idx].left { + forest[left_child].flipped ^= true; + } + if let Some(right_child) = forest[node_idx].right { + forest[right_child].flipped ^= true; + } + } +} + // makes node_idx the root of its Splay tree pub fn splay(forest: &mut [Node], node_idx: usize) { assert!(node_idx < forest.len(), "splay: node_idx out of bounds"); while let Parent::Node(parent_idx) = forest[node_idx].parent { + // unflip the node and its parent + if let Parent::Node(grandparent_idx) = forest[parent_idx].parent { + unflip(forest, grandparent_idx); + } + unflip(forest, parent_idx); + unflip(forest, node_idx); + if forest[parent_idx].left == Some(node_idx) { if let Parent::Node(grandparent_idx) = forest[parent_idx].parent { if forest[grandparent_idx].left == Some(parent_idx) { @@ -88,6 +108,7 @@ pub fn splay(forest: &mut [Node], node_idx: usize) { rotate_left(forest, parent_idx); } } + unflip(forest, node_idx); } #[cfg(test)] @@ -280,4 +301,25 @@ mod tests { assert_eq!(forest[3].right, Some(2)); assert!(matches!(forest[2].parent, node::Parent::Node(3))); } + + #[test] + fn unflip() { + let mut forest = create_nodes(3); + forest[0].flipped = true; + forest[0].left = Some(1); + forest[0].right = Some(2); + forest[1].parent = node::Parent::Node(0); + forest[2].parent = node::Parent::Node(0); + // 0 + // / \ + // 1 2 + + // unflip should swap left and right children + super::unflip(&mut forest, 0); + assert!(!forest[0].flipped); + assert_eq!(forest[0].left, Some(2)); + assert_eq!(forest[0].right, Some(1)); + assert!(forest[1].flipped); + assert!(forest[2].flipped); + } } From f48ee38b85abd75370ddc6862d402f10ff683e0e Mon Sep 17 00:00:00 2001 From: azizkayumov Date: Mon, 2 Oct 2023 23:17:22 +0900 Subject: [PATCH 04/12] findmax with root --- src/lib.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/node.rs | 5 ++++- src/splay.rs | 43 +------------------------------------- 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2cabeab..83b8181 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,22 @@ impl LinkCutTree { self.forest[v].left = None; } } + + // Finds the maximum weight in the path from v and its parent + pub fn findmax(&mut self, v: usize) -> usize { + self.access(v); + + // find the root of the tree containing v (go left as far as possible) + let mut max_idx = v; + let mut cur = v; + while let Some(left_idx) = self.forest[cur].left { + if self.forest[left_idx].weight > self.forest[max_idx].weight { + max_idx = left_idx; + } + cur = left_idx; + } + max_idx + } } #[cfg(test)] @@ -386,5 +402,46 @@ mod tests { } #[test] - pub fn findmin() {} + pub fn findmax() { + // We form a link-cut tree from a rooted tree with the following structure + // (the numbers in parentheses are the weights of the nodes): + // 0(0) + // / \ + // 1(8) 6(1) + // / \ \ + // 2(7) 3(6) 7(3) + // / \ \ + // 4(2) 5(9) 8(4) + // / + // 9(5) + let mut lctree = super::LinkCutTree::new(10); + lctree.link(1, 0); + lctree.link(2, 1); + lctree.link(3, 1); + lctree.link(4, 3); + lctree.link(5, 3); + lctree.link(6, 0); + lctree.link(7, 6); + lctree.link(8, 7); + lctree.link(9, 8); + lctree.forest[0].weight = 0.0; + lctree.forest[1].weight = 8.0; + lctree.forest[2].weight = 7.0; + lctree.forest[3].weight = 6.0; + lctree.forest[4].weight = 2.0; + lctree.forest[5].weight = 9.0; + lctree.forest[6].weight = 1.0; + lctree.forest[7].weight = 3.0; + lctree.forest[8].weight = 4.0; + lctree.forest[9].weight = 5.0; + + let ground_truth = vec![0, 1, 1, 1, 1, 5, 6, 7, 8, 9]; + for i in 0..10 { + assert_eq!(lctree.findmax(i), ground_truth[i]); + } + // reverse the order of findmax calls: + for i in 0..10 { + assert_eq!(lctree.findmax(i), ground_truth[i]); + } + } } diff --git a/src/node.rs b/src/node.rs index af67a16..fa4bb10 100644 --- a/src/node.rs +++ b/src/node.rs @@ -11,7 +11,9 @@ pub struct Node { pub right: Option, pub parent: Parent, pub flipped: bool, - pub weight: f64, // for path aggregation + // for path aggregation: + pub weight: f64, + pub max_weight_idx: usize, } impl Node { @@ -23,6 +25,7 @@ impl Node { parent: Parent::Root, flipped: false, weight, + max_weight_idx: idx, } } } diff --git a/src/splay.rs b/src/splay.rs index 8ca67fa..4b2f367 100644 --- a/src/splay.rs +++ b/src/splay.rs @@ -52,32 +52,13 @@ fn rotate_right(forest: &mut [Node], node_idx: usize) { if let Some(new_left_child) = forest[node_idx].left { forest[new_left_child].parent = Parent::Node(node_idx); } -} - -fn unflip(forest: &mut [Node], node_idx: usize) { - if forest[node_idx].flipped { - forest[node_idx].flipped = false; - std::mem::swap(&mut forest[node_idx].left, &mut forest[node_idx].right); - if let Some(left_child) = forest[node_idx].left { - forest[left_child].flipped ^= true; - } - if let Some(right_child) = forest[node_idx].right { - forest[right_child].flipped ^= true; - } - } + //update_max(forest, left_child, node_idx); } // makes node_idx the root of its Splay tree pub fn splay(forest: &mut [Node], node_idx: usize) { assert!(node_idx < forest.len(), "splay: node_idx out of bounds"); while let Parent::Node(parent_idx) = forest[node_idx].parent { - // unflip the node and its parent - if let Parent::Node(grandparent_idx) = forest[parent_idx].parent { - unflip(forest, grandparent_idx); - } - unflip(forest, parent_idx); - unflip(forest, node_idx); - if forest[parent_idx].left == Some(node_idx) { if let Parent::Node(grandparent_idx) = forest[parent_idx].parent { if forest[grandparent_idx].left == Some(parent_idx) { @@ -108,7 +89,6 @@ pub fn splay(forest: &mut [Node], node_idx: usize) { rotate_left(forest, parent_idx); } } - unflip(forest, node_idx); } #[cfg(test)] @@ -301,25 +281,4 @@ mod tests { assert_eq!(forest[3].right, Some(2)); assert!(matches!(forest[2].parent, node::Parent::Node(3))); } - - #[test] - fn unflip() { - let mut forest = create_nodes(3); - forest[0].flipped = true; - forest[0].left = Some(1); - forest[0].right = Some(2); - forest[1].parent = node::Parent::Node(0); - forest[2].parent = node::Parent::Node(0); - // 0 - // / \ - // 1 2 - - // unflip should swap left and right children - super::unflip(&mut forest, 0); - assert!(!forest[0].flipped); - assert_eq!(forest[0].left, Some(2)); - assert_eq!(forest[0].right, Some(1)); - assert!(forest[1].flipped); - assert!(forest[2].flipped); - } } From 40c36ba40213da824142432bb96eb23cce448e82 Mon Sep 17 00:00:00 2001 From: azizkayumov Date: Tue, 3 Oct 2023 00:06:12 +0900 Subject: [PATCH 05/12] failing test case --- src/lib.rs | 25 ++++++++++++++----------- src/node.rs | 15 +++++++++++++++ src/splay.rs | 8 ++++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 83b8181..4e3f4b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,18 +73,9 @@ impl LinkCutTree { // Finds the maximum weight in the path from v and its parent pub fn findmax(&mut self, v: usize) -> usize { + //self.forest[v].max_weight_idx = v; self.access(v); - - // find the root of the tree containing v (go left as far as possible) - let mut max_idx = v; - let mut cur = v; - while let Some(left_idx) = self.forest[cur].left { - if self.forest[left_idx].weight > self.forest[max_idx].weight { - max_idx = left_idx; - } - cur = left_idx; - } - max_idx + self.forest[v].max_weight_idx } } @@ -443,5 +434,17 @@ mod tests { for i in 0..10 { assert_eq!(lctree.findmax(i), ground_truth[i]); } + + for node in lctree.forest.iter() { + println!("{}", node.to_str()); + } + println!("----------------------"); + let max4 = lctree.findmax(4); + + for node in lctree.forest.iter() { + println!("{}", node.to_str()); + } + + assert_eq!(max4, 1); } } diff --git a/src/node.rs b/src/node.rs index fa4bb10..2520301 100644 --- a/src/node.rs +++ b/src/node.rs @@ -28,4 +28,19 @@ impl Node { max_weight_idx: idx, } } + + pub fn to_str(&self) -> String { + let parent = match self.parent { + Parent::Node(idx) => format!("Node({})", idx), + Parent::Path(idx) => format!("Path({})", idx), + Parent::Root => "Root".to_string(), + }; + format!( + "Node {{ idx: {}, left: {:?}, right: {:?}, parent: {:?}", + self.idx, + self.left, + self.right, + parent, + ) + } } diff --git a/src/splay.rs b/src/splay.rs index 4b2f367..16b17d3 100644 --- a/src/splay.rs +++ b/src/splay.rs @@ -1,5 +1,12 @@ use crate::node::{Node, Parent}; +fn update_max(forest: &mut [Node], node_idx: usize, new_max_idx: usize) { + let cur_max_idx = forest[node_idx].max_weight_idx; + if forest[cur_max_idx].weight < forest[new_max_idx].weight { + forest[node_idx].max_weight_idx = new_max_idx; + } +} + fn rotate_left(forest: &mut [Node], node_idx: usize) { // base cases: // - node_idx is out of bounds @@ -25,6 +32,7 @@ fn rotate_left(forest: &mut [Node], node_idx: usize) { if let Some(new_right_child) = forest[node_idx].right { forest[new_right_child].parent = Parent::Node(node_idx); } + update_max(forest, right_child, node_idx); } fn rotate_right(forest: &mut [Node], node_idx: usize) { From b576a2610d812843e6fc7bda7a66dda2d8efa8d2 Mon Sep 17 00:00:00 2001 From: azizkayumov Date: Tue, 3 Oct 2023 00:35:28 +0900 Subject: [PATCH 06/12] findmax random test --- src/lib.rs | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4e3f4b2..241386a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,7 @@ impl LinkCutTree { // Finds the maximum weight in the path from v and its parent pub fn findmax(&mut self, v: usize) -> usize { - //self.forest[v].max_weight_idx = v; + self.forest[v].max_weight_idx = v; self.access(v); self.forest[v].max_weight_idx } @@ -81,6 +81,7 @@ impl LinkCutTree { #[cfg(test)] mod tests { + use std::time; use crate::node::Parent; #[test] @@ -427,24 +428,13 @@ mod tests { lctree.forest[9].weight = 5.0; let ground_truth = vec![0, 1, 1, 1, 1, 5, 6, 7, 8, 9]; - for i in 0..10 { - assert_eq!(lctree.findmax(i), ground_truth[i]); - } - // reverse the order of findmax calls: - for i in 0..10 { - assert_eq!(lctree.findmax(i), ground_truth[i]); - } - - for node in lctree.forest.iter() { - println!("{}", node.to_str()); - } - println!("----------------------"); - let max4 = lctree.findmax(4); - - for node in lctree.forest.iter() { - println!("{}", node.to_str()); + for _ in 0..1000 { + let v = time::SystemTime::now() + .duration_since(time::UNIX_EPOCH) + .unwrap() + .as_nanos() as usize + % 10; + assert_eq!(lctree.findmax(v), ground_truth[v]); } - - assert_eq!(max4, 1); } } From 7daf10b1aa0926128bbf9416bba0511ef0fa7427 Mon Sep 17 00:00:00 2001 From: Aziz Kayumov Date: Tue, 3 Oct 2023 11:14:28 +0900 Subject: [PATCH 07/12] fix findmax bug --- Cargo.toml | 3 ++ src/lib.rs | 87 +++++++++++++++++++++++++++++++++++++++++------------ src/node.rs | 3 +- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9a6cdc6..0c4332c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +[dev-dependencies] +rand = "0.8" diff --git a/src/lib.rs b/src/lib.rs index 241386a..74bcb28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,15 +73,29 @@ impl LinkCutTree { // Finds the maximum weight in the path from v and its parent pub fn findmax(&mut self, v: usize) -> usize { - self.forest[v].max_weight_idx = v; + // let mut cur = v; + // while let Some(left_idx) = self.forest[cur].left { + // let cur_max_idx = self.forest[v].max_weight_idx; + // if self.forest[left_idx].weight > self.forest[cur_max_idx].weight { + // self.forest[v].max_weight_idx = left_idx; + // } + // cur = left_idx; + // } self.access(v); + let mut cur = v; + while let Some(left_idx) = self.forest[cur].left { + let cur_max_idx = self.forest[v].max_weight_idx; + if self.forest[left_idx].weight > self.forest[cur_max_idx].weight { + self.forest[v].max_weight_idx = left_idx; + } + cur = left_idx; + } self.forest[v].max_weight_idx } } #[cfg(test)] mod tests { - use std::time; use crate::node::Parent; #[test] @@ -160,7 +174,7 @@ mod tests { #[test] pub fn link_already_connected_with_path() { // '3' has a path pointer to '2', and '2' has a path pointer to '0': - // link(0, 1) should add no link, and result in (| denotes a path pointer): + // link(0, 3) should add no link, and result in (| denotes a path pointer): // 0 0 0 3 // / \ / | / | / // 1 2 => 1 2 => 1 3 => 0 @@ -194,7 +208,7 @@ mod tests { // 0 3 // / \ // 1 2 - // link(1, 3) should result in a single tree (| denotes a path pointer): + // link(3, 1) should result in a single tree (| denotes a path pointer): // 1 3 1 // | | \ // 0 => 0 3 @@ -397,13 +411,13 @@ mod tests { pub fn findmax() { // We form a link-cut tree from a rooted tree with the following structure // (the numbers in parentheses are the weights of the nodes): - // 0(0) + // 0(4) // / \ - // 1(8) 6(1) + // 1(6) 6(1) // / \ \ - // 2(7) 3(6) 7(3) + // 2(0) 3(7) 7(3) // / \ \ - // 4(2) 5(9) 8(4) + // 4(2) 5(9) 8(7) // / // 9(5) let mut lctree = super::LinkCutTree::new(10); @@ -416,25 +430,60 @@ mod tests { lctree.link(7, 6); lctree.link(8, 7); lctree.link(9, 8); - lctree.forest[0].weight = 0.0; - lctree.forest[1].weight = 8.0; - lctree.forest[2].weight = 7.0; - lctree.forest[3].weight = 6.0; + lctree.forest[0].weight = 4.0; + lctree.forest[1].weight = 6.0; + lctree.forest[2].weight = 0.0; + lctree.forest[3].weight = 7.0; lctree.forest[4].weight = 2.0; lctree.forest[5].weight = 9.0; lctree.forest[6].weight = 1.0; lctree.forest[7].weight = 3.0; - lctree.forest[8].weight = 4.0; + lctree.forest[8].weight = 7.0; lctree.forest[9].weight = 5.0; + let ground_truth = vec![0, 1, 1, 3, 3, 5, 0, 0, 8, 8]; - let ground_truth = vec![0, 1, 1, 1, 1, 5, 6, 7, 8, 9]; for _ in 0..1000 { - let v = time::SystemTime::now() - .duration_since(time::UNIX_EPOCH) - .unwrap() - .as_nanos() as usize - % 10; + let v = rand::random::() % 10; assert_eq!(lctree.findmax(v), ground_truth[v]); } } + + // creates a random tree with n nodes + fn create_random_tree(n: usize) -> (Vec<(usize, usize)>, Vec, Vec){ + let mut edges = Vec::new(); + let mut weights = Vec::new(); + for _ in 0..n { + let weight = (rand::random::() * n as f64).round() as f64; + weights.push(weight); + } + + let mut ground_truth = vec![0; n]; + let mut in_tree = Vec::from([0]); + for i in 1..n { + let parent_idx = rand::random::() % in_tree.len(); + let parent = in_tree[parent_idx]; + edges.push((i, parent)); + + let max_idx = if weights[i] > weights[ground_truth[parent]] { i } else { ground_truth[parent] }; + ground_truth[i] = max_idx; + + in_tree.push(i); + } + + (edges, weights, ground_truth) + } + + #[test] + pub fn findmax_random() { + let n = 7; + let (edges, weights, ground_truth) = create_random_tree(n); + let mut lctree = super::LinkCutTree::new(n); + for (v, w) in edges { + lctree.link(v, w); + } + for i in 0..n { + lctree.forest[i].weight = weights[i]; + } + + } } diff --git a/src/node.rs b/src/node.rs index 2520301..e779a44 100644 --- a/src/node.rs +++ b/src/node.rs @@ -36,11 +36,12 @@ impl Node { Parent::Root => "Root".to_string(), }; format!( - "Node {{ idx: {}, left: {:?}, right: {:?}, parent: {:?}", + "Node {{ idx: {}, left: {:?}, right: {:?}, parent: {:?}, max_weight_idx: {} }}", self.idx, self.left, self.right, parent, + self.max_weight_idx ) } } From 8e3323c19a67fb3339da93c2b436f31ebd87e61b Mon Sep 17 00:00:00 2001 From: Aziz Kayumov Date: Tue, 3 Oct 2023 12:38:14 +0900 Subject: [PATCH 08/12] clean up update max --- src/lib.rs | 46 ++++++++++++++++++++++++++++++++++++---------- src/node.rs | 6 +----- src/splay.rs | 9 --------- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 74bcb28..8670f6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,8 +85,9 @@ impl LinkCutTree { let mut cur = v; while let Some(left_idx) = self.forest[cur].left { let cur_max_idx = self.forest[v].max_weight_idx; - if self.forest[left_idx].weight > self.forest[cur_max_idx].weight { - self.forest[v].max_weight_idx = left_idx; + let left_max_idx = self.forest[left_idx].max_weight_idx; + if self.forest[left_max_idx].weight > self.forest[cur_max_idx].weight { + self.forest[v].max_weight_idx = left_max_idx; } cur = left_idx; } @@ -96,6 +97,8 @@ impl LinkCutTree { #[cfg(test)] mod tests { + use rand::seq::SliceRandom; + use crate::node::Parent; #[test] @@ -449,13 +452,10 @@ mod tests { } // creates a random tree with n nodes - fn create_random_tree(n: usize) -> (Vec<(usize, usize)>, Vec, Vec){ + fn create_random_tree(n: usize) -> (Vec<(usize, usize)>, Vec, Vec) { let mut edges = Vec::new(); - let mut weights = Vec::new(); - for _ in 0..n { - let weight = (rand::random::() * n as f64).round() as f64; - weights.push(weight); - } + let mut weights: Vec = (0..n).map(|i| i as f64).collect(); + weights.shuffle(&mut rand::thread_rng()); let mut ground_truth = vec![0; n]; let mut in_tree = Vec::from([0]); @@ -464,12 +464,16 @@ mod tests { let parent = in_tree[parent_idx]; edges.push((i, parent)); - let max_idx = if weights[i] > weights[ground_truth[parent]] { i } else { ground_truth[parent] }; + let max_idx = if weights[i] >= weights[ground_truth[parent]] { + i + } else { + ground_truth[parent] + }; ground_truth[i] = max_idx; in_tree.push(i); } - + (edges, weights, ground_truth) } @@ -478,6 +482,11 @@ mod tests { let n = 7; let (edges, weights, ground_truth) = create_random_tree(n); let mut lctree = super::LinkCutTree::new(n); + + println!("weights: {:?}", weights); + println!("edges: {:?}", edges); + println!("ground truth: {:?}", ground_truth); + for (v, w) in edges { lctree.link(v, w); } @@ -485,5 +494,22 @@ mod tests { lctree.forest[i].weight = weights[i]; } + for _ in 0..1000 { + let v = rand::random::() % n; + println!("v = {}", v); + println!("-----------------------------"); + for node in &lctree.forest { + println!("{}", node.to_str()); + } + + let findmax_result = lctree.findmax(v); + + println!("-----------------------------"); + for node in &lctree.forest { + println!("{}", node.to_str()); + } + + assert_eq!(findmax_result, ground_truth[v]); + } } } diff --git a/src/node.rs b/src/node.rs index e779a44..36d9ef3 100644 --- a/src/node.rs +++ b/src/node.rs @@ -37,11 +37,7 @@ impl Node { }; format!( "Node {{ idx: {}, left: {:?}, right: {:?}, parent: {:?}, max_weight_idx: {} }}", - self.idx, - self.left, - self.right, - parent, - self.max_weight_idx + self.idx, self.left, self.right, parent, self.max_weight_idx ) } } diff --git a/src/splay.rs b/src/splay.rs index 16b17d3..ab0fcb8 100644 --- a/src/splay.rs +++ b/src/splay.rs @@ -1,12 +1,5 @@ use crate::node::{Node, Parent}; -fn update_max(forest: &mut [Node], node_idx: usize, new_max_idx: usize) { - let cur_max_idx = forest[node_idx].max_weight_idx; - if forest[cur_max_idx].weight < forest[new_max_idx].weight { - forest[node_idx].max_weight_idx = new_max_idx; - } -} - fn rotate_left(forest: &mut [Node], node_idx: usize) { // base cases: // - node_idx is out of bounds @@ -32,7 +25,6 @@ fn rotate_left(forest: &mut [Node], node_idx: usize) { if let Some(new_right_child) = forest[node_idx].right { forest[new_right_child].parent = Parent::Node(node_idx); } - update_max(forest, right_child, node_idx); } fn rotate_right(forest: &mut [Node], node_idx: usize) { @@ -60,7 +52,6 @@ fn rotate_right(forest: &mut [Node], node_idx: usize) { if let Some(new_left_child) = forest[node_idx].left { forest[new_left_child].parent = Parent::Node(node_idx); } - //update_max(forest, left_child, node_idx); } // makes node_idx the root of its Splay tree From 2896bec1eb336fdab7ad7478e9b37c4f0484f8ed Mon Sep 17 00:00:00 2001 From: Aziz Kayumov Date: Tue, 3 Oct 2023 15:23:14 +0900 Subject: [PATCH 09/12] findmax seems to be working --- src/lib.rs | 217 +++++++++++++++++++++++++-------------------------- src/splay.rs | 25 ++++++ 2 files changed, 133 insertions(+), 109 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8670f6b..b3ee414 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ mod node; mod path; mod splay; +use splay::update_max; + use crate::{ node::{Node, Parent}, splay::splay, @@ -39,19 +41,31 @@ impl LinkCutTree { // attach the node as the path parent's right child self.forest[path_idx].right = Some(v); self.forest[v].parent = Parent::Node(path_idx); + + // update aggregate information + update_max(&mut self.forest, path_idx); + splay(&mut self.forest, v); } + + // update aggregate information + update_max(&mut self.forest, v); } - // Creates a link between two nodes in the forest (w becomes the parent of v) + // Creates a link between two nodes in the forest (v becomes the parent of w) pub fn link(&mut self, v: usize, w: usize) { self.access(v); self.access(w); if !matches!(self.forest[v].parent, Parent::Root) || v == w { return; // already connected } - self.forest[w].right = Some(v); - self.forest[v].parent = Parent::Node(w); + assert!(matches!(self.forest[v].left, None)); + self.forest[v].left = Some(w); + self.forest[w].parent = Parent::Node(v); + + // update aggregate information + update_max(&mut self.forest, w); + update_max(&mut self.forest, v); } // Checks if v and w are connected in the forest @@ -69,37 +83,22 @@ impl LinkCutTree { self.forest[left].parent = Parent::Root; self.forest[v].left = None; } + + // update aggregate information + update_max(&mut self.forest, v); } // Finds the maximum weight in the path from v and its parent pub fn findmax(&mut self, v: usize) -> usize { - // let mut cur = v; - // while let Some(left_idx) = self.forest[cur].left { - // let cur_max_idx = self.forest[v].max_weight_idx; - // if self.forest[left_idx].weight > self.forest[cur_max_idx].weight { - // self.forest[v].max_weight_idx = left_idx; - // } - // cur = left_idx; - // } self.access(v); - let mut cur = v; - while let Some(left_idx) = self.forest[cur].left { - let cur_max_idx = self.forest[v].max_weight_idx; - let left_max_idx = self.forest[left_idx].max_weight_idx; - if self.forest[left_max_idx].weight > self.forest[cur_max_idx].weight { - self.forest[v].max_weight_idx = left_max_idx; - } - cur = left_idx; - } self.forest[v].max_weight_idx } } #[cfg(test)] mod tests { - use rand::seq::SliceRandom; - use crate::node::Parent; + use rand::{seq::SliceRandom, Rng, SeedableRng}; #[test] pub fn access_base_case() { @@ -212,30 +211,31 @@ mod tests { // / \ // 1 2 // link(3, 1) should result in a single tree (| denotes a path pointer): - // 1 3 1 - // | | \ - // 0 => 0 3 - // \ \ - // 2 2 - // + // 1 3 3 + // | / + // 0 => 1 + // \ | + // 2 0 + // \ + // 2 let mut tree = super::LinkCutTree::new(4); tree.forest[0].left = Some(1); tree.forest[0].right = Some(2); tree.forest[1].parent = Parent::Node(0); tree.forest[2].parent = Parent::Node(0); tree.link(3, 1); - assert!(matches!(tree.forest[1].parent, Parent::Root)); - assert_eq!(tree.forest[1].right, Some(3)); - assert_eq!(tree.forest[1].left, None); - assert!(matches!(tree.forest[3].parent, Parent::Node(1))); + assert!(matches!(tree.forest[3].parent, Parent::Root)); + assert_eq!(tree.forest[3].left, Some(1)); assert_eq!(tree.forest[3].right, None); - assert_eq!(tree.forest[3].left, None); + assert!(matches!(tree.forest[1].parent, Parent::Node(3))); + assert_eq!(tree.forest[1].left, None); + assert_eq!(tree.forest[1].right, None); assert!(matches!(tree.forest[0].parent, Parent::Path(1))); - assert_eq!(tree.forest[0].right, Some(2)); assert_eq!(tree.forest[0].left, None); + assert_eq!(tree.forest[0].right, Some(2)); assert!(matches!(tree.forest[2].parent, Parent::Node(0))); - assert_eq!(tree.forest[2].right, None); assert_eq!(tree.forest[2].left, None); + assert_eq!(tree.forest[2].right, None); } #[test] @@ -260,7 +260,7 @@ mod tests { #[test] pub fn connected_with_path_pointers() { - // check two trees that are connected by a path pointer + // check two splay trees that are connected by a path pointer // 0 // / \ // 1 2 @@ -284,13 +284,13 @@ mod tests { assert!(!tree.connected(0, 1)); // not connected yet tree.link(0, 1); - assert!(matches!(tree.forest[1].parent, Parent::Root)); - assert_eq!(tree.forest[1].right, Some(0)); - assert_eq!(tree.forest[1].left, None); - assert!(matches!(tree.forest[0].parent, Parent::Node(1))); - // 1 - // \ <= link(0, 1) // 0 + // / <= link(0, 1) + // 1 + assert!(matches!(tree.forest[0].parent, Parent::Root)); + assert_eq!(tree.forest[0].right, None); + assert_eq!(tree.forest[0].left, Some(1)); + assert!(matches!(tree.forest[1].parent, Parent::Node(0))); assert!(tree.connected(0, 1)); // now connected assert!(matches!(tree.forest[1].parent, Parent::Root)); @@ -327,19 +327,21 @@ mod tests { // 2 tree.link(2, 3); // link(2, 3) should result in: - // 3 - // | \ - // 4 2 - // | - // 1 - // \ - // 0 - assert!(matches!(tree.forest[3].parent, Parent::Root)); - assert!(matches!(tree.forest[4].parent, Parent::Path(3))); - assert_eq!(tree.forest[3].right, Some(2)); - assert!(matches!(tree.forest[2].parent, Parent::Node(3))); + // 2 + // / | + // 3 1 + // | \ + // 4 0 + assert!(matches!(tree.forest[2].parent, Parent::Root)); + assert_eq!(tree.forest[2].left, Some(3)); + assert_eq!(tree.forest[2].right, None); + assert!(matches!(tree.forest[3].parent, Parent::Node(2))); assert!(matches!(tree.forest[1].parent, Parent::Path(2))); + assert_eq!(tree.forest[3].left, None); + assert_eq!(tree.forest[3].right, None); + assert!(matches!(tree.forest[4].parent, Parent::Path(3))); assert_eq!(tree.forest[1].right, Some(0)); + assert!(matches!(tree.forest[0].parent, Parent::Node(1))); assert!(tree.connected(2, 3)); // we cut node 2 from its parent 3: @@ -412,55 +414,63 @@ mod tests { #[test] pub fn findmax() { - // We form a link-cut tree from a rooted tree with the following structure - // (the numbers in parentheses are the weights of the nodes): - // 0(4) - // / \ - // 1(6) 6(1) - // / \ \ - // 2(0) 3(7) 7(3) - // / \ \ - // 4(2) 5(9) 8(7) - // / - // 9(5) - let mut lctree = super::LinkCutTree::new(10); + let mut lctree = super::LinkCutTree::new(7); + lctree.forest[0].weight = 5.0; + lctree.forest[1].weight = 2.0; + lctree.forest[2].weight = 6.0; + lctree.forest[3].weight = 3.0; + lctree.forest[4].weight = 4.0; + lctree.forest[5].weight = 0.0; + lctree.forest[6].weight = 1.0; + + // we form the following structure: + // 0(5) + // / + // 1(2) + // / + // 2(6) + // / + // 3(3) + // / \ + // 4(4) 6(1) + // / + // 5(0) + lctree.link(1, 0); lctree.link(2, 1); - lctree.link(3, 1); + lctree.link(3, 2); lctree.link(4, 3); - lctree.link(5, 3); - lctree.link(6, 0); - lctree.link(7, 6); - lctree.link(8, 7); - lctree.link(9, 8); - lctree.forest[0].weight = 4.0; - lctree.forest[1].weight = 6.0; - lctree.forest[2].weight = 0.0; - lctree.forest[3].weight = 7.0; - lctree.forest[4].weight = 2.0; - lctree.forest[5].weight = 9.0; - lctree.forest[6].weight = 1.0; - lctree.forest[7].weight = 3.0; - lctree.forest[8].weight = 7.0; - lctree.forest[9].weight = 5.0; - let ground_truth = vec![0, 1, 1, 3, 3, 5, 0, 0, 8, 8]; + lctree.link(5, 4); + lctree.link(6, 3); + + assert_eq!(lctree.findmax(2), 2); + assert_eq!(lctree.findmax(6), 2); + assert_eq!(lctree.findmax(0), 0); + assert_eq!(lctree.findmax(5), 2); + assert_eq!(lctree.findmax(3), 2); + assert_eq!(lctree.findmax(1), 0); + assert_eq!(lctree.findmax(4), 2); + } - for _ in 0..1000 { - let v = rand::random::() % 10; - assert_eq!(lctree.findmax(v), ground_truth[v]); + fn print_lctree(lctree: &super::LinkCutTree) { + println!("-----------------------------------"); + for node in lctree.forest.iter() { + println!("{:?}", node.to_str()); } } // creates a random tree with n nodes - fn create_random_tree(n: usize) -> (Vec<(usize, usize)>, Vec, Vec) { + fn create_random_tree(n: usize, state: u64) -> (Vec<(usize, usize)>, Vec, Vec) { + let mut rng = rand::rngs::StdRng::seed_from_u64(state); + let mut edges = Vec::new(); let mut weights: Vec = (0..n).map(|i| i as f64).collect(); - weights.shuffle(&mut rand::thread_rng()); + weights.shuffle(&mut rng); let mut ground_truth = vec![0; n]; let mut in_tree = Vec::from([0]); for i in 1..n { - let parent_idx = rand::random::() % in_tree.len(); + let parent_idx = rng.gen_range(0..in_tree.len()); let parent = in_tree[parent_idx]; edges.push((i, parent)); @@ -479,9 +489,14 @@ mod tests { #[test] pub fn findmax_random() { - let n = 7; - let (edges, weights, ground_truth) = create_random_tree(n); + let n = 100; + let seed = rand::thread_rng().gen(); + println!("seed = {}", seed); + let (edges, weights, ground_truth) = create_random_tree(n, seed); let mut lctree = super::LinkCutTree::new(n); + for i in 0..n { + lctree.forest[i].weight = weights[i]; + } println!("weights: {:?}", weights); println!("edges: {:?}", edges); @@ -490,26 +505,10 @@ mod tests { for (v, w) in edges { lctree.link(v, w); } - for i in 0..n { - lctree.forest[i].weight = weights[i]; - } - - for _ in 0..1000 { - let v = rand::random::() % n; - println!("v = {}", v); - println!("-----------------------------"); - for node in &lctree.forest { - println!("{}", node.to_str()); - } - let findmax_result = lctree.findmax(v); - - println!("-----------------------------"); - for node in &lctree.forest { - println!("{}", node.to_str()); - } - - assert_eq!(findmax_result, ground_truth[v]); + for _ in 0..1000000 { + let v = rand::thread_rng().gen_range(0..n); + assert_eq!(lctree.findmax(v), ground_truth[v]); } } } diff --git a/src/splay.rs b/src/splay.rs index ab0fcb8..e23d4cb 100644 --- a/src/splay.rs +++ b/src/splay.rs @@ -25,6 +25,10 @@ fn rotate_left(forest: &mut [Node], node_idx: usize) { if let Some(new_right_child) = forest[node_idx].right { forest[new_right_child].parent = Parent::Node(node_idx); } + + // update aggregate information + update_max(forest, node_idx); + update_max(forest, right_child); } fn rotate_right(forest: &mut [Node], node_idx: usize) { @@ -52,6 +56,27 @@ fn rotate_right(forest: &mut [Node], node_idx: usize) { if let Some(new_left_child) = forest[node_idx].left { forest[new_left_child].parent = Parent::Node(node_idx); } + + // update aggregate information + update_max(forest, node_idx); + update_max(forest, left_child); +} + +pub fn update_max(forest: &mut [Node], node_idx: usize) { + let mut max_idx = node_idx; + if let Some(left_child) = forest[node_idx].left { + let left_max = forest[left_child].max_weight_idx; + if forest[left_max].weight > forest[max_idx].weight { + max_idx = left_max; + } + } + if let Some(right_child) = forest[node_idx].right { + let right_max = forest[right_child].max_weight_idx; + if forest[right_max].weight > forest[max_idx].weight { + max_idx = right_max; + } + } + forest[node_idx].max_weight_idx = max_idx; } // makes node_idx the root of its Splay tree From 33fd7a1b704922e26de304a9f9f32543446b1092 Mon Sep 17 00:00:00 2001 From: Aziz Kayumov Date: Tue, 3 Oct 2023 17:30:39 +0900 Subject: [PATCH 10/12] clean up findmax test --- src/lib.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b3ee414..d095565 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -452,13 +452,6 @@ mod tests { assert_eq!(lctree.findmax(4), 2); } - fn print_lctree(lctree: &super::LinkCutTree) { - println!("-----------------------------------"); - for node in lctree.forest.iter() { - println!("{:?}", node.to_str()); - } - } - // creates a random tree with n nodes fn create_random_tree(n: usize, state: u64) -> (Vec<(usize, usize)>, Vec, Vec) { let mut rng = rand::rngs::StdRng::seed_from_u64(state); @@ -506,7 +499,7 @@ mod tests { lctree.link(v, w); } - for _ in 0..1000000 { + for _ in 0..n * 10 { let v = rand::thread_rng().gen_range(0..n); assert_eq!(lctree.findmax(v), ground_truth[v]); } From 2c02cd55a16dc9daf3b78041f14773b7fa9f8e72 Mon Sep 17 00:00:00 2001 From: azizkayumov Date: Tue, 3 Oct 2023 22:54:05 +0900 Subject: [PATCH 11/12] findmax --- src/lib.rs | 116 +++++++++++++++++++++++------------------------------ 1 file changed, 50 insertions(+), 66 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d095565..dc50993 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,9 +42,6 @@ impl LinkCutTree { self.forest[path_idx].right = Some(v); self.forest[v].parent = Parent::Node(path_idx); - // update aggregate information - update_max(&mut self.forest, path_idx); - splay(&mut self.forest, v); } @@ -52,20 +49,16 @@ impl LinkCutTree { update_max(&mut self.forest, v); } - // Creates a link between two nodes in the forest (v becomes the parent of w) + /// Creates a link between two nodes in the forest (v becomes the parent of w) + /// # Panics if v has a left child (i.e. v is not a root node) pub fn link(&mut self, v: usize, w: usize) { self.access(v); self.access(w); if !matches!(self.forest[v].parent, Parent::Root) || v == w { return; // already connected } - assert!(matches!(self.forest[v].left, None)); - self.forest[v].left = Some(w); - self.forest[w].parent = Parent::Node(v); - - // update aggregate information - update_max(&mut self.forest, w); - update_max(&mut self.forest, v); + self.forest[w].right = Some(v); + self.forest[v].parent = Parent::Node(w); } // Checks if v and w are connected in the forest @@ -83,9 +76,6 @@ impl LinkCutTree { self.forest[left].parent = Parent::Root; self.forest[v].left = None; } - - // update aggregate information - update_max(&mut self.forest, v); } // Finds the maximum weight in the path from v and its parent @@ -110,7 +100,7 @@ mod tests { #[test] pub fn access_leaf() { - let mut tree = super::LinkCutTree::new(3); + let mut tree = super::LinkCutTree::new(4); // '1' has a path pointer to '0', '1' has a right child '2'. // after access(2), '2' should be the root of the tree: // 0 0 0 2 @@ -211,31 +201,23 @@ mod tests { // / \ // 1 2 // link(3, 1) should result in a single tree (| denotes a path pointer): - // 1 3 3 - // | / - // 0 => 1 - // \ | - // 2 0 - // \ - // 2 + // 1 3 1 + // | |\ + // 0 => 0 3 + // \ \ + // 2 2 let mut tree = super::LinkCutTree::new(4); tree.forest[0].left = Some(1); tree.forest[0].right = Some(2); tree.forest[1].parent = Parent::Node(0); tree.forest[2].parent = Parent::Node(0); tree.link(3, 1); - assert!(matches!(tree.forest[3].parent, Parent::Root)); - assert_eq!(tree.forest[3].left, Some(1)); - assert_eq!(tree.forest[3].right, None); - assert!(matches!(tree.forest[1].parent, Parent::Node(3))); + assert!(matches!(tree.forest[1].parent, Parent::Root)); assert_eq!(tree.forest[1].left, None); - assert_eq!(tree.forest[1].right, None); + assert_eq!(tree.forest[1].right, Some(3)); assert!(matches!(tree.forest[0].parent, Parent::Path(1))); assert_eq!(tree.forest[0].left, None); assert_eq!(tree.forest[0].right, Some(2)); - assert!(matches!(tree.forest[2].parent, Parent::Node(0))); - assert_eq!(tree.forest[2].left, None); - assert_eq!(tree.forest[2].right, None); } #[test] @@ -284,29 +266,26 @@ mod tests { assert!(!tree.connected(0, 1)); // not connected yet tree.link(0, 1); - // 0 - // / <= link(0, 1) - // 1 - assert!(matches!(tree.forest[0].parent, Parent::Root)); - assert_eq!(tree.forest[0].right, None); - assert_eq!(tree.forest[0].left, Some(1)); - assert!(matches!(tree.forest[1].parent, Parent::Node(0))); + // 1 + // \ <= link(0, 1) + // 0 + assert!(matches!(tree.forest[1].parent, Parent::Root)); + assert_eq!(tree.forest[1].left, None); + assert_eq!(tree.forest[1].right, Some(0)); + assert!(matches!(tree.forest[0].parent, Parent::Node(1))); assert!(tree.connected(0, 1)); // now connected assert!(matches!(tree.forest[1].parent, Parent::Root)); assert_eq!(tree.forest[1].right, None); assert_eq!(tree.forest[1].left, None); assert!(matches!(tree.forest[0].parent, Parent::Path(1))); - // 0 1 - // / => | - // 1 0 tree.cut(0); assert!(matches!(tree.forest[0].parent, Parent::Root)); assert_eq!(tree.forest[0].right, None); - // 0 0 - // / => - // 1 1 + // 1 1 + // \ => + // 0 0 assert!(!tree.connected(0, 1)); // now disconnected } @@ -327,19 +306,22 @@ mod tests { // 2 tree.link(2, 3); // link(2, 3) should result in: - // 2 - // / | - // 3 1 - // | \ - // 4 0 - assert!(matches!(tree.forest[2].parent, Parent::Root)); - assert_eq!(tree.forest[2].left, Some(3)); - assert_eq!(tree.forest[2].right, None); - assert!(matches!(tree.forest[3].parent, Parent::Node(2))); - assert!(matches!(tree.forest[1].parent, Parent::Path(2))); + // 3 + // | \ + // 4 2 + // | + // 1 + // \ + // 0 + assert!(matches!(tree.forest[3].parent, Parent::Root)); assert_eq!(tree.forest[3].left, None); - assert_eq!(tree.forest[3].right, None); + assert_eq!(tree.forest[3].right, Some(2)); assert!(matches!(tree.forest[4].parent, Parent::Path(3))); + assert!(matches!(tree.forest[2].parent, Parent::Node(3))); + assert_eq!(tree.forest[2].left, None); + assert_eq!(tree.forest[2].right, None); + assert!(matches!(tree.forest[1].parent, Parent::Path(2))); + assert_eq!(tree.forest[1].left, None); assert_eq!(tree.forest[1].right, Some(0)); assert!(matches!(tree.forest[0].parent, Parent::Node(1))); assert!(tree.connected(2, 3)); @@ -450,11 +432,20 @@ mod tests { assert_eq!(lctree.findmax(3), 2); assert_eq!(lctree.findmax(1), 0); assert_eq!(lctree.findmax(4), 2); + + // we cut node 3 from its parent 2: + lctree.cut(3); + assert_eq!(lctree.findmax(5), 4); + assert_eq!(lctree.findmax(6), 3); + assert_eq!(lctree.findmax(4), 4); + assert_eq!(lctree.findmax(3), 3); + assert_eq!(lctree.findmax(2), 2); + assert_eq!(lctree.findmax(1), 0); + assert_eq!(lctree.findmax(0), 0); } - // creates a random tree with n nodes - fn create_random_tree(n: usize, state: u64) -> (Vec<(usize, usize)>, Vec, Vec) { - let mut rng = rand::rngs::StdRng::seed_from_u64(state); + fn create_random_tree(n: usize, seed: u64) -> (Vec<(usize, usize)>, Vec, Vec) { + let mut rng = rand::rngs::StdRng::seed_from_u64(seed); let mut edges = Vec::new(); let mut weights: Vec = (0..n).map(|i| i as f64).collect(); @@ -467,13 +458,11 @@ mod tests { let parent = in_tree[parent_idx]; edges.push((i, parent)); - let max_idx = if weights[i] >= weights[ground_truth[parent]] { + ground_truth[i] = if weights[i] > weights[ground_truth[parent]] { i } else { ground_truth[parent] }; - ground_truth[i] = max_idx; - in_tree.push(i); } @@ -484,22 +473,17 @@ mod tests { pub fn findmax_random() { let n = 100; let seed = rand::thread_rng().gen(); - println!("seed = {}", seed); let (edges, weights, ground_truth) = create_random_tree(n, seed); let mut lctree = super::LinkCutTree::new(n); for i in 0..n { lctree.forest[i].weight = weights[i]; } - println!("weights: {:?}", weights); - println!("edges: {:?}", edges); - println!("ground truth: {:?}", ground_truth); - for (v, w) in edges { lctree.link(v, w); } - for _ in 0..n * 10 { + for _ in 0..n * 100 { let v = rand::thread_rng().gen_range(0..n); assert_eq!(lctree.findmax(v), ground_truth[v]); } From 2c55c8331ab07cdf739aa0e94b9f5a883bc9c44b Mon Sep 17 00:00:00 2001 From: azizkayumov Date: Tue, 3 Oct 2023 22:56:59 +0900 Subject: [PATCH 12/12] make clippy happy again --- src/node.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node.rs b/src/node.rs index 36d9ef3..6b3585c 100644 --- a/src/node.rs +++ b/src/node.rs @@ -31,8 +31,8 @@ impl Node { pub fn to_str(&self) -> String { let parent = match self.parent { - Parent::Node(idx) => format!("Node({})", idx), - Parent::Path(idx) => format!("Path({})", idx), + Parent::Node(idx) => format!("Node({idx})"), + Parent::Path(idx) => format!("Path({idx})"), Parent::Root => "Root".to_string(), }; format!(