use std::collections::HashSet;
use std::hash::Hash;
use std::sync::RwLock;
pub struct PartitionController<NodeId> {
isolated: RwLock<HashSet<NodeId>>,
cut_edges: RwLock<HashSet<(NodeId, NodeId)>>,
}
impl<NodeId> PartitionController<NodeId>
where
NodeId: Eq + Hash + Copy,
{
pub fn new() -> Self {
Self {
isolated: RwLock::new(HashSet::new()),
cut_edges: RwLock::new(HashSet::new()),
}
}
pub fn isolate(&self, node: NodeId) {
self.isolated.write().unwrap().insert(node);
}
pub fn heal(&self, node: NodeId) {
self.isolated.write().unwrap().remove(&node);
}
pub fn cut_edge(&self, from: NodeId, to: NodeId) {
self.cut_edges.write().unwrap().insert((from, to));
}
pub fn restore_edge(&self, from: NodeId, to: NodeId) {
self.cut_edges.write().unwrap().remove(&(from, to));
}
pub fn is_reachable(&self, from: NodeId, to: NodeId) -> bool {
let isolated = self.isolated.read().unwrap();
if isolated.contains(&from) || isolated.contains(&to) {
return false;
}
!self.cut_edges.read().unwrap().contains(&(from, to))
}
}
impl<NodeId> Default for PartitionController<NodeId>
where
NodeId: Eq + Hash + Copy,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_fully_reachable() {
let p = PartitionController::<u64>::new();
assert!(p.is_reachable(1, 2));
assert!(p.is_reachable(2, 1));
assert!(p.is_reachable(1, 1));
}
#[test]
fn isolate_blocks_both_directions() {
let p = PartitionController::<u64>::new();
p.isolate(1);
assert!(!p.is_reachable(1, 2));
assert!(!p.is_reachable(2, 1));
assert!(p.is_reachable(2, 3));
}
#[test]
fn heal_restores_node_level_reachability() {
let p = PartitionController::<u64>::new();
p.isolate(1);
p.heal(1);
assert!(p.is_reachable(1, 2));
assert!(p.is_reachable(2, 1));
}
#[test]
fn cut_edge_blocks_only_one_direction() {
let p = PartitionController::<u64>::new();
p.cut_edge(1, 2);
assert!(!p.is_reachable(1, 2));
assert!(p.is_reachable(2, 1));
}
#[test]
fn restore_edge_undoes_cut() {
let p = PartitionController::<u64>::new();
p.cut_edge(1, 2);
p.restore_edge(1, 2);
assert!(p.is_reachable(1, 2));
}
#[test]
fn heal_does_not_restore_individually_cut_edges() {
let p = PartitionController::<u64>::new();
p.cut_edge(1, 2);
p.isolate(1);
p.heal(1);
assert!(!p.is_reachable(1, 2));
assert!(p.is_reachable(2, 1));
}
}