use crate::graph::VertexId;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
pub type EdgeKey = (VertexId, VertexId);
#[inline]
fn normalize_edge(u: VertexId, v: VertexId) -> EdgeKey {
if u < v {
(u, v)
} else {
(v, u)
}
}
#[derive(Debug, Clone)]
pub struct ReplacementEdgeIndex {
max_level: usize,
level_edges: Vec<BTreeSet<EdgeKey>>,
edge_level: HashMap<EdgeKey, usize>,
tree_edges: HashSet<EdgeKey>,
level_adjacency: Vec<HashMap<VertexId, BTreeSet<VertexId>>>,
component_size: HashMap<VertexId, usize>,
}
impl ReplacementEdgeIndex {
pub fn new(n: usize) -> Self {
let max_level = (n as f64).log2().ceil() as usize + 1;
let max_level = max_level.max(1);
Self {
max_level,
level_edges: vec![BTreeSet::new(); max_level],
edge_level: HashMap::new(),
tree_edges: HashSet::new(),
level_adjacency: vec![HashMap::new(); max_level],
component_size: HashMap::new(),
}
}
pub fn add_tree_edge(&mut self, u: VertexId, v: VertexId) {
let key = normalize_edge(u, v);
self.tree_edges.insert(key);
}
pub fn remove_tree_edge(&mut self, u: VertexId, v: VertexId) -> bool {
let key = normalize_edge(u, v);
self.tree_edges.remove(&key)
}
pub fn add_non_tree_edge(&mut self, u: VertexId, v: VertexId) {
let key = normalize_edge(u, v);
if self.tree_edges.contains(&key) {
return;
}
if self.level_edges[0].insert(key) {
self.edge_level.insert(key, 0);
self.level_adjacency[0].entry(u).or_default().insert(v);
self.level_adjacency[0].entry(v).or_default().insert(u);
}
}
pub fn remove_non_tree_edge(&mut self, u: VertexId, v: VertexId) {
let key = normalize_edge(u, v);
if let Some(level) = self.edge_level.remove(&key) {
self.level_edges[level].remove(&key);
if let Some(adj) = self.level_adjacency[level].get_mut(&u) {
adj.remove(&v);
}
if let Some(adj) = self.level_adjacency[level].get_mut(&v) {
adj.remove(&u);
}
}
}
pub fn find_replacement(
&mut self,
u: VertexId,
v: VertexId,
tree_adjacency: &HashMap<VertexId, HashSet<VertexId>>,
) -> Option<EdgeKey> {
let key = normalize_edge(u, v);
if !self.tree_edges.contains(&key) {
return None;
}
let (smaller_vertices, _larger_vertices) =
self.find_components_after_cut(u, v, tree_adjacency);
for level in (0..self.max_level).rev() {
if let Some(replacement) = self.find_replacement_at_level(level, &smaller_vertices) {
return Some(replacement);
}
}
self.promote_edges(&smaller_vertices);
None
}
pub fn find_replacement_fast(&self, smaller_component: &HashSet<VertexId>) -> Option<EdgeKey> {
for level in 0..self.max_level {
if let Some(replacement) = self.find_replacement_at_level(level, smaller_component) {
return Some(replacement);
}
}
None
}
fn find_replacement_at_level(
&self,
level: usize,
component: &HashSet<VertexId>,
) -> Option<EdgeKey> {
for &vertex in component {
if let Some(neighbors) = self.level_adjacency[level].get(&vertex) {
for &neighbor in neighbors {
if !component.contains(&neighbor) {
return Some(normalize_edge(vertex, neighbor));
}
}
}
}
None
}
fn find_components_after_cut(
&self,
u: VertexId,
v: VertexId,
tree_adj: &HashMap<VertexId, HashSet<VertexId>>,
) -> (HashSet<VertexId>, HashSet<VertexId>) {
let mut comp_u = HashSet::new();
let mut stack = vec![u];
comp_u.insert(u);
while let Some(x) = stack.pop() {
if let Some(neighbors) = tree_adj.get(&x) {
for &y in neighbors {
if (x == u && y == v) || (x == v && y == u) {
continue;
}
if comp_u.insert(y) {
stack.push(y);
}
}
}
}
let mut comp_v = HashSet::new();
stack.push(v);
comp_v.insert(v);
while let Some(x) = stack.pop() {
if let Some(neighbors) = tree_adj.get(&x) {
for &y in neighbors {
if (x == u && y == v) || (x == v && y == u) {
continue;
}
if comp_v.insert(y) {
stack.push(y);
}
}
}
}
if comp_u.len() <= comp_v.len() {
(comp_u, comp_v)
} else {
(comp_v, comp_u)
}
}
fn promote_edges(&mut self, component: &HashSet<VertexId>) {
let mut to_promote = Vec::new();
for level in 0..self.max_level.saturating_sub(1) {
for &vertex in component {
if let Some(neighbors) = self.level_adjacency[level].get(&vertex).cloned() {
for neighbor in neighbors {
if component.contains(&neighbor) {
let key = normalize_edge(vertex, neighbor);
to_promote.push((key, level));
}
}
}
}
}
for (key, old_level) in to_promote {
let new_level = (old_level + 1).min(self.max_level - 1);
if new_level != old_level {
let (u, v) = key;
self.level_edges[old_level].remove(&key);
if let Some(adj) = self.level_adjacency[old_level].get_mut(&u) {
adj.remove(&v);
}
if let Some(adj) = self.level_adjacency[old_level].get_mut(&v) {
adj.remove(&u);
}
self.level_edges[new_level].insert(key);
self.edge_level.insert(key, new_level);
self.level_adjacency[new_level]
.entry(u)
.or_default()
.insert(v);
self.level_adjacency[new_level]
.entry(v)
.or_default()
.insert(u);
}
}
}
pub fn stats(&self) -> ReplacementIndexStats {
let edges_per_level: Vec<usize> = self.level_edges.iter().map(|s| s.len()).collect();
ReplacementIndexStats {
max_level: self.max_level,
tree_edges: self.tree_edges.len(),
non_tree_edges: self.edge_level.len(),
edges_per_level,
}
}
}
#[derive(Debug, Clone)]
pub struct ReplacementIndexStats {
pub max_level: usize,
pub tree_edges: usize,
pub non_tree_edges: usize,
pub edges_per_level: Vec<usize>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_index() {
let idx = ReplacementEdgeIndex::new(100);
assert!(idx.max_level >= 7); assert_eq!(idx.tree_edges.len(), 0);
}
#[test]
fn test_add_tree_edge() {
let mut idx = ReplacementEdgeIndex::new(10);
idx.add_tree_edge(1, 2);
idx.add_tree_edge(2, 3);
assert!(idx.tree_edges.contains(&(1, 2)));
assert!(idx.tree_edges.contains(&(2, 3)));
}
#[test]
fn test_add_non_tree_edge() {
let mut idx = ReplacementEdgeIndex::new(10);
idx.add_tree_edge(1, 2);
idx.add_non_tree_edge(1, 3);
idx.add_non_tree_edge(2, 4);
assert!(idx.level_edges[0].contains(&(1, 3)));
assert!(idx.level_edges[0].contains(&(2, 4)));
assert_eq!(idx.edge_level.get(&(1, 3)), Some(&0));
}
#[test]
fn test_find_replacement_simple() {
let mut idx = ReplacementEdgeIndex::new(10);
idx.add_tree_edge(1, 2);
idx.add_tree_edge(2, 3);
idx.add_non_tree_edge(1, 3);
let mut tree_adj: HashMap<VertexId, HashSet<VertexId>> = HashMap::new();
tree_adj.entry(1).or_default().insert(2);
tree_adj.entry(2).or_default().insert(1);
tree_adj.entry(2).or_default().insert(3);
tree_adj.entry(3).or_default().insert(2);
let replacement = idx.find_replacement(1, 2, &tree_adj);
assert_eq!(replacement, Some((1, 3)));
}
#[test]
fn test_find_replacement_none() {
let mut idx = ReplacementEdgeIndex::new(10);
idx.add_tree_edge(1, 2);
idx.add_tree_edge(2, 3);
let mut tree_adj: HashMap<VertexId, HashSet<VertexId>> = HashMap::new();
tree_adj.entry(1).or_default().insert(2);
tree_adj.entry(2).or_default().insert(1);
tree_adj.entry(2).or_default().insert(3);
tree_adj.entry(3).or_default().insert(2);
let replacement = idx.find_replacement(1, 2, &tree_adj);
assert!(replacement.is_none());
}
#[test]
fn test_find_replacement_fast() {
let mut idx = ReplacementEdgeIndex::new(10);
idx.add_non_tree_edge(1, 4);
idx.add_non_tree_edge(2, 5);
let component: HashSet<VertexId> = [1, 2, 3].into_iter().collect();
let replacement = idx.find_replacement_fast(&component);
assert!(replacement.is_some());
let (u, v) = replacement.unwrap();
assert!(component.contains(&u) != component.contains(&v));
}
#[test]
fn test_stats() {
let mut idx = ReplacementEdgeIndex::new(100);
idx.add_tree_edge(1, 2);
idx.add_tree_edge(2, 3);
idx.add_non_tree_edge(1, 3);
idx.add_non_tree_edge(3, 4);
let stats = idx.stats();
assert_eq!(stats.tree_edges, 2);
assert_eq!(stats.non_tree_edges, 2);
assert_eq!(stats.edges_per_level[0], 2);
}
#[test]
fn test_remove_edges() {
let mut idx = ReplacementEdgeIndex::new(10);
idx.add_tree_edge(1, 2);
idx.add_non_tree_edge(1, 3);
assert!(idx.remove_tree_edge(1, 2));
assert!(!idx.tree_edges.contains(&(1, 2)));
idx.remove_non_tree_edge(1, 3);
assert!(!idx.level_edges[0].contains(&(1, 3)));
}
}