use std::collections::HashSet;
use serde::{Deserialize, Serialize};
use crate::traits::BackboneStrategy;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct UnionFind {
parent: Vec<usize>,
rank: Vec<usize>,
num_components: usize,
}
impl UnionFind {
fn new(n: usize) -> Self {
Self {
parent: (0..n).collect(),
rank: vec![0; n],
num_components: n,
}
}
fn ensure_capacity(&mut self, n: usize) {
while self.parent.len() < n {
let id = self.parent.len();
self.parent.push(id);
self.rank.push(0);
self.num_components += 1;
}
}
fn find(&mut self, mut x: usize) -> usize {
while self.parent[x] != x {
self.parent[x] = self.parent[self.parent[x]]; x = self.parent[x];
}
x
}
fn union(&mut self, a: usize, b: usize) -> bool {
let ra = self.find(a);
let rb = self.find(b);
if ra == rb {
return false;
}
match self.rank[ra].cmp(&self.rank[rb]) {
std::cmp::Ordering::Less => self.parent[ra] = rb,
std::cmp::Ordering::Greater => self.parent[rb] = ra,
std::cmp::Ordering::Equal => {
self.parent[rb] = ra;
self.rank[ra] += 1;
}
}
self.num_components -= 1;
true
}
fn connected(&mut self, a: usize, b: usize) -> bool {
self.find(a) == self.find(b)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Backbone {
uf: UnionFind,
backbone_edges: HashSet<(usize, usize)>,
non_backbone_edges: HashSet<(usize, usize)>,
}
impl Backbone {
pub fn new(n: usize) -> Self {
Self {
uf: UnionFind::new(n),
backbone_edges: HashSet::new(),
non_backbone_edges: HashSet::new(),
}
}
#[inline]
fn edge_key(u: usize, v: usize) -> (usize, usize) {
if u <= v { (u, v) } else { (v, u) }
}
fn rebuild_uf(&mut self) {
let n = self.uf.parent.len();
self.uf = UnionFind::new(n);
for &(u, v) in &self.backbone_edges {
self.uf.union(u, v);
}
}
}
impl BackboneStrategy for Backbone {
fn insert_edge(&mut self, u: usize, v: usize, _weight: f64) -> bool {
let key = Self::edge_key(u, v);
self.ensure_capacity(u.max(v) + 1);
if !self.uf.connected(u, v) {
self.uf.union(u, v);
self.backbone_edges.insert(key);
true
} else {
self.non_backbone_edges.insert(key);
false
}
}
fn delete_edge(&mut self, u: usize, v: usize, _weight: f64) -> bool {
let key = Self::edge_key(u, v);
if self.non_backbone_edges.remove(&key) {
return false;
}
if !self.backbone_edges.remove(&key) {
return false;
}
self.rebuild_uf();
let mut replacement = None;
for &(a, b) in &self.non_backbone_edges {
if !self.uf.connected(a, b) {
replacement = Some((a, b));
break;
}
}
if let Some((a, b)) = replacement {
let rkey = Self::edge_key(a, b);
self.non_backbone_edges.remove(&rkey);
self.backbone_edges.insert(rkey);
self.uf.union(a, b);
}
true
}
fn is_backbone_edge(&self, u: usize, v: usize) -> bool {
self.backbone_edges.contains(&Self::edge_key(u, v))
}
fn num_components(&self) -> usize {
self.uf.num_components
}
fn connected(&mut self, u: usize, v: usize) -> bool {
if u >= self.uf.parent.len() || v >= self.uf.parent.len() {
return false;
}
self.uf.connected(u, v)
}
fn backbone_edge_count(&self) -> usize {
self.backbone_edges.len()
}
fn ensure_capacity(&mut self, n: usize) {
self.uf.ensure_capacity(n);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_backbone_connectivity() {
let mut bb = Backbone::new(4);
assert!(bb.insert_edge(0, 1, 1.0)); assert!(bb.insert_edge(1, 2, 1.0)); assert!(bb.insert_edge(2, 3, 1.0)); assert!(!bb.insert_edge(0, 3, 1.0));
assert_eq!(bb.num_components(), 1);
assert_eq!(bb.backbone_edge_count(), 3);
assert!(bb.is_backbone_edge(0, 1));
assert!(!bb.is_backbone_edge(0, 3));
}
#[test]
fn test_backbone_deletion_with_replacement() {
let mut bb = Backbone::new(3);
bb.insert_edge(0, 1, 1.0); bb.insert_edge(1, 2, 1.0); bb.insert_edge(0, 2, 1.0);
assert_eq!(bb.backbone_edge_count(), 2);
let modified = bb.delete_edge(0, 1, 1.0);
assert!(modified);
assert_eq!(bb.num_components(), 1);
assert!(bb.is_backbone_edge(0, 2));
}
#[test]
fn test_backbone_deletion_no_replacement() {
let mut bb = Backbone::new(3);
bb.insert_edge(0, 1, 1.0);
bb.insert_edge(1, 2, 1.0);
let modified = bb.delete_edge(0, 1, 1.0);
assert!(modified);
assert_eq!(bb.num_components(), 2);
}
#[test]
fn test_auto_grow() {
let mut bb = Backbone::new(0);
assert!(bb.insert_edge(5, 10, 1.0));
assert!(bb.connected(5, 10));
assert_eq!(bb.backbone_edge_count(), 1);
}
}