Skip to content

Commit

Permalink
Merge pull request #16 from azizkayumov/test
Browse files Browse the repository at this point in the history
Connectivity random test
  • Loading branch information
azizkayumov committed Oct 4, 2023
2 parents 9c5717e + 9d2e59d commit d7f5cdc
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 48 deletions.
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.

0 comments on commit d7f5cdc

Please sign in to comment.