Skip to content

Commit

Permalink
Merge pull request #49 from azizkayumov/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
azizkayumov committed Nov 11, 2023
2 parents fefca8b + 80944b8 commit 336eed2
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 230 deletions.
99 changes: 51 additions & 48 deletions src/lctree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,32 @@ impl<P: Path> LinkCutTree<P> {
self.forest.parent_of(v).is_some() || v == w
}

/// Checks if two nodes are connected by a link
/// (i.e. v is the parent of w or vice versa).
///
/// # Examples
/// ```
/// use lctree::LinkCutTree;
///
/// let mut lctree = LinkCutTree::default();
/// let alice = lctree.make_tree(0.0);
/// let bob = lctree.make_tree(0.0);
/// let clay = lctree.make_tree(0.0);
///
/// lctree.link(alice, bob);
/// lctree.link(bob, clay);
///
/// assert!(lctree.linked(alice, bob)); // alice and bob are connected by a link
/// assert!(!lctree.linked(alice, clay)); // alice and clay are not connected by a link
/// ```
pub fn linked(&mut self, v: usize, w: usize) -> bool {
if self.connected(v, w) {
self.forest.left_of(w) == Some(v) && self.forest.right_of(v).is_none()
} else {
false
}
}

/// Merges two trees into a single tree.
///
/// # Examples
Expand All @@ -182,12 +208,13 @@ impl<P: Path> LinkCutTree<P> {
/// lctree.link(bob, clay);
/// assert!(lctree.connected(alice, clay));
/// ```
pub fn link(&mut self, v: usize, w: usize) {
pub fn link(&mut self, v: usize, w: usize) -> bool {
if self.connected(v, w) {
return;
return false;
}
// v is the root of its represented tree:
self.forest.set_left(v, w);
true
}

/// Cuts the link between two nodes (if it exists)
Expand All @@ -207,19 +234,12 @@ impl<P: Path> LinkCutTree<P> {
/// lctree.cut(alice, bob);
/// assert!(!lctree.connected(alice, bob)); // not connected again
/// ```
pub fn cut(&mut self, v: usize, w: usize) {
if !self.connected(v, w) {
return;
}
// detach w from its parent (which is v)
if let Some(left) = self.forest.left_of(w) {
if left != v || self.forest.right_of(v).is_some() {
// maybe this should be a panic?
// eprintln!("Error: no link between {v} and {w}");
return;
}
self.forest.cut_left(w);
pub fn cut(&mut self, v: usize, w: usize) -> bool {
if !self.linked(v, w) {
return false;
}
self.forest.cut_left(w);
true
}

/// Performs path aggregation on a path between two nodes (if they are connected)
Expand Down Expand Up @@ -335,60 +355,43 @@ mod tests {
}

#[test]
pub fn invalid_link() {
pub fn connected_so_no_need_to_link() {
let mut lctree = super::LinkCutTree::default();
let alice = lctree.make_tree(0.0);
let bob = lctree.make_tree(10.0);
let clay = lctree.make_tree(2.0);
lctree.link(alice, bob);
lctree.link(bob, clay);
lctree.link(alice, clay); // should do nothing

// bob should have the max weight in the path from alice to clay:
let heaviest_node = lctree.path(alice, clay);
assert_eq!(heaviest_node.idx, bob);
assert_eq!(heaviest_node.weight, 10.0);
// Try to link two nodes that are already connected:
assert!(!lctree.link(alice, clay));
}

#[test]
pub fn invalid_cut() {
pub fn connected_but_no_edge_to_cut() {
let mut lctree = super::LinkCutTree::default();
let alice = lctree.make_tree(0.0);
let bob = lctree.make_tree(10.0);
let clay = lctree.make_tree(2.0);
lctree.link(alice, bob);

let path = lctree.path(alice, clay);
lctree.cut(alice, clay); // should do nothing
let same_path = lctree.path(alice, clay);
// path from alice to clay should be the same as before:
assert_eq!(path.idx, same_path.idx);
assert_eq!(path.weight, same_path.weight);
lctree.link(bob, clay);
// Try to cut an edge that doesn't exist:
assert!(!lctree.cut(alice, clay));
}

#[test]
pub fn connected_but_no_edge_to_cut() {
// We form a link-cut tree from the following rooted tree:
// a
// / \
// b c
// \
// d
pub fn linked() {
let mut lctree = super::LinkCutTree::default();
let a = lctree.make_tree(0.0);
let b = lctree.make_tree(0.0);
let c = lctree.make_tree(0.0);
let d = lctree.make_tree(0.0);

lctree.link(b, a);
lctree.link(c, a);
lctree.link(d, c);
let alice = lctree.make_tree(0.0);
let bob = lctree.make_tree(0.0);
let clay = lctree.make_tree(0.0);

// Try to cut non-existing edge:
lctree.cut(d, a); // should do nothing
lctree.link(alice, bob);
lctree.link(bob, clay);

// They should still be connected:
assert!(lctree.connected(d, a));
assert!(lctree.linked(alice, bob));
assert!(lctree.linked(bob, clay));
// alice and clay are not connected by a link
assert!(!lctree.linked(alice, clay));
}

#[test]
Expand Down
9 changes: 3 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@
//! This crate implements link-cut tree for unrooted trees, which means all of the above operations
//! can be performed on any two nodes in the forest.
//!
//! # Path aggregation
//! Common path aggregates are provided as follows:
//! - `findmax(v, w)`: returns the maximum value on a path between nodes `v` and `w`.
//! - `findmin(v, w)`: returns the minimum value on a path between nodes `v` and `w`.
//! - `findsum(v, w)`: returns the sum of values on a path between nodes `v` and `w`.
//! A custom aggregation function can also be implemented by using the [Path] trait.
//! # Path operations
//! The most common path aggregates are supported: `FindMax`, `FindMin`, and `FindSum`.
//! A custom path aggregate function can be implemented by using the [Path] trait.
//!
//! # Tree creation and removal
//! Tree nodes are created and removed using the following operations:
Expand Down
Loading

0 comments on commit 336eed2

Please sign in to comment.