Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connectivity random test #16

Merged
merged 2 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ edition = "2021"

[dev-dependencies]
rand = "0.8"
rand_derive2 = "0.1.21"
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ impl LinkCutTree {
}

/// 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);
Expand Down
112 changes: 112 additions & 0 deletions tests/test_connectivity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use lctree::LinkCutTree;
use rand::{rngs::StdRng, seq::IteratorRandom, Rng, SeedableRng};
use rand_derive2::RandGen;
use std::collections::HashSet;

fn create_random_generator() -> StdRng {
let seed = rand::thread_rng().gen();
println!("Seed: {}", seed); // print seed so we can reproduce the test (if it fails).
StdRng::seed_from_u64(seed)
}

fn create_random_tree(rng: &mut StdRng) -> Vec<(usize, usize)> {
let mut nodes = Vec::from([0]);
let mut edges = Vec::new();
for i in 1..NUMBER_OF_NODES {
let parent = nodes[rng.gen_range(0..i)];
nodes.push(i);
edges.push((i, parent));
}
edges
}

fn dfs(
v: usize,
adj: &Vec<Vec<usize>>,
visited: &mut Vec<bool>,
component_ids: &mut Vec<usize>,
component_id: usize,
) {
visited[v] = true;
component_ids[v] = component_id;
for &w in &adj[v] {
if !visited[w] {
dfs(w, adj, visited, component_ids, component_id);
}
}
}

fn connected_components(edges: &HashSet<(usize, usize)>) -> Vec<usize> {
// create adjacency list from edges
let mut adj = vec![vec![]; NUMBER_OF_NODES];
for (v, w) in edges {
adj[*v].push(*w);
adj[*w].push(*v);
}

// explore each component using dfs and assign component ids
let mut visited = vec![false; NUMBER_OF_NODES];
let mut component_ids = vec![0; NUMBER_OF_NODES];
let mut component_id = 0;
for v in 0..NUMBER_OF_NODES {
if !visited[v] {
component_id += 1;
dfs(v, &adj, &mut visited, &mut component_ids, component_id);
}
}
component_ids
}

const NUMBER_OF_NODES: usize = 100;
const NUMBER_OF_OPERATIONS: usize = 1000;

#[derive(RandGen)]
enum Operation {
Link,
Cut,
Connected,
}

#[test]
pub fn test_connectivity() {
let mut rng = create_random_generator();
let edges = create_random_tree(&mut rng);

// initialize link-cut tree, we start with a forest of single nodes
// (edges are not added yet):
let mut lctree = LinkCutTree::new(NUMBER_OF_NODES);
let mut edges_in_tree = HashSet::new();
let mut component_ids = (0..NUMBER_OF_NODES).collect::<Vec<usize>>();

// perform random operations: link, cut, or connected:
for _ in 0..NUMBER_OF_OPERATIONS {
let operation: Operation = rng.gen();
match operation {
Operation::Link => {
let (v, w) = edges.iter().choose(&mut rng).unwrap();
println!("Link {} {}", v, w);
lctree.link(*v, *w);

edges_in_tree.insert((*v, *w));
component_ids = connected_components(&edges_in_tree);
}
Operation::Cut => {
if edges_in_tree.is_empty() {
continue;
}
let (v, w) = edges_in_tree.iter().choose(&mut rng).unwrap();
println!("Cut {} {}", v, w);
lctree.cut(*v);

edges_in_tree.remove(&(*v, *w));
component_ids = connected_components(&edges_in_tree);
}
Operation::Connected => {
let v = rng.gen_range(0..NUMBER_OF_NODES);
let w = rng.gen_range(0..NUMBER_OF_NODES);
println!("Connected {} {}", v, w);
assert_eq!(lctree.connected(v, w), component_ids[v] == component_ids[w]);
}
}
}
}
55 changes: 55 additions & 0 deletions tests/test_findmax.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use lctree::LinkCutTree;
use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng};

fn create_random_generator() -> StdRng {
let seed = rand::thread_rng().gen();
println!("Seed: {}", seed); // print seed so we can reproduce the test (if it fails).
StdRng::seed_from_u64(seed)
}

fn create_random_tree(rng: &mut StdRng) -> (Vec<(usize, usize)>, Vec<f64>, Vec<usize>) {
let mut edges = Vec::new();
let mut weights: Vec<f64> = (0..NUMBER_OF_NODES).map(|i| i as f64).collect();
weights.shuffle(rng);

let mut max_to_root = vec![0; NUMBER_OF_NODES];
let mut in_tree = Vec::from([0]);
for i in 1..NUMBER_OF_NODES {
let parent_idx = rng.gen_range(0..in_tree.len());
let parent = in_tree[parent_idx];
edges.push((i, parent));

max_to_root[i] = if weights[i] > weights[max_to_root[parent]] {
i
} else {
max_to_root[parent]
};
in_tree.push(i);
}

(edges, weights, max_to_root)
}

const NUMBER_OF_NODES: usize = 1000;

#[test]
pub fn findmax_random() {
let mut rng = create_random_generator();
let (edges, weights, expected_findmax) = create_random_tree(&mut rng);

// initialize link-cut tree
let mut lctree = LinkCutTree::new(NUMBER_OF_NODES);
for i in 0..NUMBER_OF_NODES {
lctree.set_weight(i, weights[i]);
}
for (v, w) in edges {
lctree.link(v, w);
}

// perform random findmax queries
let mut nodes = (0..NUMBER_OF_NODES).collect::<Vec<usize>>();
nodes.shuffle(&mut rng);
for v in nodes {
assert_eq!(lctree.findmax(v), expected_findmax[v]);
}
}
47 changes: 0 additions & 47 deletions tests/test_findmax_random.rs

This file was deleted.