diff --git a/README.md b/README.md index 86cfa68..bdbaa7a 100644 --- a/README.md +++ b/README.md @@ -118,8 +118,30 @@ impl Path for FindXor { } fn main() { + // We form a link-cut tree from the following rooted tree + // (the numbers in parentheses are the weights of the nodes): + // a(9) + // / \ + // b(1) e(2) + // / \ \ + // c(8) d(10) f(4) let mut lctree: LinkCutTree = LinkCutTree::new(); - ... + let a = lctree.make_tree(9.); + let b = lctree.make_tree(1.); + let c = lctree.make_tree(8.); + let d = lctree.make_tree(10.); + let e = lctree.make_tree(2.); + let f = lctree.make_tree(4.); + + lctree.link(b, a); + lctree.link(c, b); + lctree.link(d, b); + lctree.link(e, a); + lctree.link(f, e); + + // We find the xor of the weights on the path between c to f, + let result = lctree.path(c, f); + assert_eq!(result.xor, 8 ^ 1 ^ 9 ^ 2 ^ 4); } ``` diff --git a/tests/test_random.rs b/tests/test_random.rs index 2aec2c4..b3871bc 100644 --- a/tests/test_random.rs +++ b/tests/test_random.rs @@ -1,81 +1,11 @@ -use lctree::{FindMax, LinkCutTree}; +use lctree::LinkCutTree; use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; use rand_derive2::RandGen; use std::collections::{HashMap, HashSet}; -#[test] -pub fn basic_usage() { - // We form a link-cut tree from the following rooted tree: - // a - // / \ - // b e - // / \ \ - // c d f - let mut lctree = lctree::LinkCutTree::default(); - let a = lctree.make_tree(0.0); - let b = lctree.make_tree(1.0); - let c = lctree.make_tree(2.0); - let d = lctree.make_tree(3.0); - let e = lctree.make_tree(4.0); - let f = lctree.make_tree(5.0); - lctree.link(b, a); - lctree.link(c, b); - lctree.link(d, b); - lctree.link(e, a); - lctree.link(f, e); - - // Checking connectivity: - assert!(lctree.connected(c, f)); // connected - - // We cut node e from its parent a: - lctree.cut(e, a); - - // The forest should now look like this: - // a - // / - // b e - // / \ \ - // c d f - - // We check connectivity again: - assert!(!lctree.connected(c, f)); // not connected anymore -} - -#[test] -pub fn path_aggregation() { - // We form a link-cut tree from the following rooted tree - // (the numbers in parentheses are the weights of the nodes): - // a(9) - // / \ - // b(1) e(2) - // / \ \ - // c(8) d(0) f(4) - - // Replace FindMax with FindMin or FindSum, depending on your usage: - let mut lctree: LinkCutTree = lctree::LinkCutTree::new(); - let a = lctree.make_tree(9.); - let b = lctree.make_tree(1.); - let c = lctree.make_tree(8.); - let d = lctree.make_tree(0.); - let e = lctree.make_tree(2.); - let f = lctree.make_tree(4.); - - lctree.link(b, a); - lctree.link(c, b); - lctree.link(d, b); - lctree.link(e, a); - lctree.link(f, e); - - // We find the node with max weight on the path between c to f, - // where a has the maximum weight of 9.0: - let heaviest_node = lctree.path(c, f); - assert_eq!(heaviest_node.idx, a); - assert_eq!(heaviest_node.weight, 9.0); -} - #[test] pub fn validation() { - // These can be larger if you have time to spare (see tests/README.md) + // These can be larger if you have time to spare: let num_nodes: usize = 100; let num_operations: usize = 2000; diff --git a/tests/test_usage.rs b/tests/test_usage.rs new file mode 100644 index 0000000..504a147 --- /dev/null +++ b/tests/test_usage.rs @@ -0,0 +1,114 @@ +use lctree::{FindMax, LinkCutTree, Path}; + +#[test] +pub fn basic_usage() { + // We form a link-cut tree from the following rooted tree: + // a + // / \ + // b e + // / \ \ + // c d f + let mut lctree = lctree::LinkCutTree::default(); + let a = lctree.make_tree(0.); + let b = lctree.make_tree(0.); + let c = lctree.make_tree(0.); + let d = lctree.make_tree(0.); + let e = lctree.make_tree(0.); + let f = lctree.make_tree(0.); + lctree.link(b, a); + lctree.link(c, b); + lctree.link(d, b); + lctree.link(e, a); + lctree.link(f, e); + + // Checking connectivity: + assert!(lctree.connected(c, f)); // connected + + // We cut node e from its parent a: + lctree.cut(e, a); + + // The forest should now look like this: + // a + // / + // b e + // / \ \ + // c d f + + // We check connectivity again: + assert!(!lctree.connected(c, f)); // not connected anymore +} + +#[test] +pub fn path_aggregation() { + // We form a link-cut tree from the following rooted tree + // (the numbers in parentheses are the weights of the nodes): + // a(9) + // / \ + // b(1) e(2) + // / \ \ + // c(8) d(10) f(4) + + // Replace FindMax with FindMin or FindSum, depending on your usage: + let mut lctree: LinkCutTree = lctree::LinkCutTree::new(); + let a = lctree.make_tree(9.); + let b = lctree.make_tree(1.); + let c = lctree.make_tree(8.); + let d = lctree.make_tree(10.); + let e = lctree.make_tree(2.); + let f = lctree.make_tree(4.); + + lctree.link(b, a); + lctree.link(c, b); + lctree.link(d, b); + lctree.link(e, a); + lctree.link(f, e); + + // We find the node with max weight on the path between c to f, + // where a has the maximum weight of 9.0: + let heaviest_node = lctree.path(c, f); + assert_eq!(heaviest_node.idx, a); + assert_eq!(heaviest_node.weight, 9.0); +} + +#[derive(Copy, Clone)] +pub struct FindXor { + pub xor: u64, +} + +impl Path for FindXor { + fn default(weight: f64, _: usize) -> Self { + FindXor { xor: weight as u64 } + } + + fn aggregate(&mut self, other: Self) { + self.xor ^= other.xor; + } +} + +#[test] +pub fn custom_path_aggregation() { + // We form a link-cut tree from the following rooted tree + // (the numbers in parentheses are the weights of the nodes): + // a(9) + // / \ + // b(1) e(2) + // / \ \ + // c(8) d(10) f(4) + let mut lctree: LinkCutTree = LinkCutTree::new(); + let a = lctree.make_tree(9.); + let b = lctree.make_tree(1.); + let c = lctree.make_tree(8.); + let d = lctree.make_tree(10.); + let e = lctree.make_tree(2.); + let f = lctree.make_tree(4.); + + lctree.link(b, a); + lctree.link(c, b); + lctree.link(d, b); + lctree.link(e, a); + lctree.link(f, e); + + // We find the xor of the weights on the path between c to f, + let result = lctree.path(c, f); + assert_eq!(result.xor, 8 ^ 1 ^ 9 ^ 2 ^ 4); +}