ruvector_mincut/connectivity/
mod.rs

1//! Dynamic Connectivity for minimum cut wrapper
2//!
3//! Hybrid implementation using Euler Tour Trees with union-find fallback.
4//! Provides O(log n) operations for insertions and queries.
5//!
6//! # Overview
7//!
8//! This module provides dynamic connectivity data structures:
9//!
10//! - [`DynamicConnectivity`]: Euler Tour Tree backend with union-find fallback
11//!   - Edge insertions in O(log n) time
12//!   - Edge deletions via full rebuild in O(m·α(n)) time
13//!   - Connectivity queries in O(log n) time
14//!
15//! - [`PolylogConnectivity`]: Polylogarithmic worst-case connectivity (arXiv:2510.08297)
16//!   - Edge insertions in O(log³ n) expected worst-case
17//!   - Edge deletions in O(log³ n) expected worst-case
18//!   - Connectivity queries in O(log n) worst-case
19//!
20//! # Implementation
21//!
22//! The primary backend uses Euler Tour Trees for O(log n) operations.
23//! Falls back to union-find rebuild for deletions until full ETT cut is implemented.
24//!
25//! The polylog backend uses a hierarchy of O(log n) levels with edge sparsification
26//! via low-congestion shortcuts for guaranteed worst-case bounds.
27
28pub mod polylog;
29pub mod cache_opt;
30
31use std::collections::{HashMap, HashSet};
32use crate::graph::VertexId;
33use crate::euler::EulerTourTree;
34
35/// Dynamic connectivity data structure with Euler Tour Tree backend
36///
37/// Maintains connected components of an undirected graph with support for
38/// edge insertions and deletions. Uses Euler Tour Trees for O(log n) operations
39/// with union-find fallback for robustness.
40///
41/// # Examples
42///
43/// ```ignore
44/// let mut dc = DynamicConnectivity::new();
45/// dc.add_vertex(0);
46/// dc.add_vertex(1);
47/// dc.add_vertex(2);
48///
49/// dc.insert_edge(0, 1);
50/// assert!(dc.connected(0, 1));
51/// assert!(!dc.connected(0, 2));
52///
53/// dc.insert_edge(1, 2);
54/// assert!(dc.is_connected()); // All vertices connected
55///
56/// dc.delete_edge(1, 2);
57/// assert!(!dc.connected(0, 2));
58/// ```
59#[derive(Debug, Clone)]
60pub struct DynamicConnectivity {
61    /// Union-find parent array
62    parent: HashMap<VertexId, VertexId>,
63
64    /// Union-find rank array for union by rank
65    rank: HashMap<VertexId, usize>,
66
67    /// Current edge set for rebuild on deletions
68    /// Edges normalized so smaller vertex is always first
69    edges: HashSet<(VertexId, VertexId)>,
70
71    /// Number of vertices
72    vertex_count: usize,
73
74    /// Number of connected components
75    component_count: usize,
76
77    /// Euler Tour Tree for O(log n) operations
78    ett: EulerTourTree,
79
80    /// Whether ETT is in sync with union-find
81    ett_synced: bool,
82}
83
84impl DynamicConnectivity {
85    /// Creates a new empty dynamic connectivity structure
86    ///
87    /// # Examples
88    ///
89    /// ```ignore
90    /// let dc = DynamicConnectivity::new();
91    /// assert_eq!(dc.component_count(), 0);
92    /// ```
93    pub fn new() -> Self {
94        Self {
95            parent: HashMap::new(),
96            rank: HashMap::new(),
97            edges: HashSet::new(),
98            vertex_count: 0,
99            component_count: 0,
100            ett: EulerTourTree::new(),
101            ett_synced: true,
102        }
103    }
104
105    /// Adds a vertex to the connectivity structure
106    ///
107    /// If the vertex already exists, this is a no-op.
108    /// Each new vertex starts in its own component.
109    ///
110    /// # Arguments
111    ///
112    /// * `v` - The vertex ID to add
113    ///
114    /// # Examples
115    ///
116    /// ```ignore
117    /// let mut dc = DynamicConnectivity::new();
118    /// dc.add_vertex(0);
119    /// assert_eq!(dc.component_count(), 1);
120    /// ```
121    pub fn add_vertex(&mut self, v: VertexId) {
122        if !self.parent.contains_key(&v) {
123            self.parent.insert(v, v);
124            self.rank.insert(v, 0);
125            self.vertex_count += 1;
126            self.component_count += 1;
127
128            // Add to Euler Tour Tree (O(log n))
129            let _ = self.ett.make_tree(v);
130        }
131    }
132
133    /// Inserts an edge between two vertices
134    ///
135    /// Automatically adds vertices if they don't exist.
136    /// If vertices are already connected, updates internal state but
137    /// doesn't change connectivity.
138    ///
139    /// # Arguments
140    ///
141    /// * `u` - First vertex
142    /// * `v` - Second vertex
143    ///
144    /// # Time Complexity
145    ///
146    /// O(log n) via Euler Tour Tree link operation
147    ///
148    /// # Examples
149    ///
150    /// ```ignore
151    /// let mut dc = DynamicConnectivity::new();
152    /// dc.insert_edge(0, 1);
153    /// assert!(dc.connected(0, 1));
154    /// ```
155    pub fn insert_edge(&mut self, u: VertexId, v: VertexId) {
156        // Add vertices if they don't exist
157        self.add_vertex(u);
158        self.add_vertex(v);
159
160        // Normalize edge (smaller vertex first)
161        let edge = if u < v { (u, v) } else { (v, u) };
162
163        // Add to edge set
164        if self.edges.insert(edge) {
165            // New edge - perform union
166            let root_u = self.find(u);
167            let root_v = self.find(v);
168
169            if root_u != root_v {
170                self.union(root_u, root_v);
171
172                // Link in Euler Tour Tree (O(log n))
173                let _ = self.ett.link(u, v);
174            }
175        }
176    }
177
178    /// Deletes an edge between two vertices
179    ///
180    /// Triggers a full rebuild of the data structure from the remaining edges.
181    /// The ETT is also rebuilt to maintain O(log n) queries.
182    ///
183    /// # Arguments
184    ///
185    /// * `u` - First vertex
186    /// * `v` - Second vertex
187    ///
188    /// # Time Complexity
189    ///
190    /// O(m·α(n)) where m is the number of edges (includes ETT rebuild)
191    ///
192    /// # Examples
193    ///
194    /// ```ignore
195    /// let mut dc = DynamicConnectivity::new();
196    /// dc.insert_edge(0, 1);
197    /// dc.delete_edge(0, 1);
198    /// assert!(!dc.connected(0, 1));
199    /// ```
200    pub fn delete_edge(&mut self, u: VertexId, v: VertexId) {
201        // Normalize edge
202        let edge = if u < v { (u, v) } else { (v, u) };
203
204        // Remove from edge set
205        if self.edges.remove(&edge) {
206            // Mark ETT as out of sync
207            self.ett_synced = false;
208
209            // Rebuild the entire structure (including ETT)
210            self.rebuild();
211        }
212    }
213
214    /// Checks if the entire graph is connected (single component)
215    ///
216    /// # Returns
217    ///
218    /// `true` if all vertices are in a single connected component,
219    /// `false` otherwise
220    ///
221    /// # Time Complexity
222    ///
223    /// O(1)
224    ///
225    /// # Examples
226    ///
227    /// ```ignore
228    /// let mut dc = DynamicConnectivity::new();
229    /// dc.add_vertex(0);
230    /// dc.add_vertex(1);
231    /// assert!(!dc.is_connected());
232    ///
233    /// dc.insert_edge(0, 1);
234    /// assert!(dc.is_connected());
235    /// ```
236    pub fn is_connected(&self) -> bool {
237        self.component_count == 1
238    }
239
240    /// Checks if two vertices are in the same connected component
241    ///
242    /// # Arguments
243    ///
244    /// * `u` - First vertex
245    /// * `v` - Second vertex
246    ///
247    /// # Returns
248    ///
249    /// `true` if vertices are connected, `false` otherwise.
250    /// Returns `false` if either vertex doesn't exist.
251    ///
252    /// # Time Complexity
253    ///
254    /// O(α(n)) amortized via union-find with path compression
255    ///
256    /// # Examples
257    ///
258    /// ```ignore
259    /// let mut dc = DynamicConnectivity::new();
260    /// dc.insert_edge(0, 1);
261    /// dc.insert_edge(1, 2);
262    /// assert!(dc.connected(0, 2));
263    /// ```
264    pub fn connected(&mut self, u: VertexId, v: VertexId) -> bool {
265        if !self.parent.contains_key(&u) || !self.parent.contains_key(&v) {
266            return false;
267        }
268
269        // Use union-find with path compression (effectively O(1) amortized)
270        // ETT is maintained for future subtree query optimizations
271        self.find(u) == self.find(v)
272    }
273
274    /// Fast connectivity check using Euler Tour Tree (O(log n))
275    ///
276    /// Returns None if ETT is out of sync and result is unreliable.
277    /// Use `connected()` for the reliable version.
278    #[inline]
279    pub fn connected_fast(&self, u: VertexId, v: VertexId) -> Option<bool> {
280        if !self.ett_synced {
281            return None;
282        }
283        Some(self.ett.connected(u, v))
284    }
285
286    /// Returns the number of connected components
287    ///
288    /// # Returns
289    ///
290    /// The current number of connected components
291    pub fn component_count(&self) -> usize {
292        self.component_count
293    }
294
295    /// Returns the number of vertices
296    ///
297    /// # Returns
298    ///
299    /// The current number of vertices
300    pub fn vertex_count(&self) -> usize {
301        self.vertex_count
302    }
303
304    /// Finds the root of a vertex's component with path compression
305    ///
306    /// # Arguments
307    ///
308    /// * `v` - The vertex to find the root for
309    ///
310    /// # Returns
311    ///
312    /// The root vertex of the component containing `v`
313    ///
314    /// # Panics
315    ///
316    /// Panics if the vertex doesn't exist in the structure
317    fn find(&mut self, v: VertexId) -> VertexId {
318        let parent = *self.parent.get(&v).expect("Vertex not found");
319
320        if parent != v {
321            // Path compression: make v point directly to root
322            let root = self.find(parent);
323            self.parent.insert(v, root);
324            root
325        } else {
326            v
327        }
328    }
329
330    /// Unions two components by rank
331    ///
332    /// # Arguments
333    ///
334    /// * `u` - Root of first component
335    /// * `v` - Root of second component
336    ///
337    /// # Notes
338    ///
339    /// This function assumes `u` and `v` are roots. It should only be
340    /// called after `find()` operations.
341    fn union(&mut self, u: VertexId, v: VertexId) {
342        if u == v {
343            return;
344        }
345
346        let rank_u = *self.rank.get(&u).unwrap_or(&0);
347        let rank_v = *self.rank.get(&v).unwrap_or(&0);
348
349        // Union by rank: attach smaller tree to larger tree
350        if rank_u < rank_v {
351            self.parent.insert(u, v);
352        } else if rank_u > rank_v {
353            self.parent.insert(v, u);
354        } else {
355            // Equal rank: arbitrary choice, increment rank
356            self.parent.insert(v, u);
357            self.rank.insert(u, rank_u + 1);
358        }
359
360        // Decrease component count
361        self.component_count -= 1;
362    }
363
364    /// Rebuilds the union-find structure from the current edge set
365    ///
366    /// Called after edge deletions to recompute connected components.
367    /// Resets all vertices to singleton components and re-applies all edges.
368    /// Also rebuilds the Euler Tour Tree for O(log n) queries.
369    ///
370    /// # Time Complexity
371    ///
372    /// O(m·α(n)) where m is the number of edges
373    fn rebuild(&mut self) {
374        // Collect all vertices
375        let vertices: Vec<VertexId> = self.parent.keys().copied().collect();
376
377        // Reset to singleton components
378        self.component_count = vertices.len();
379        for &v in &vertices {
380            self.parent.insert(v, v);
381            self.rank.insert(v, 0);
382        }
383
384        // Rebuild Euler Tour Tree
385        self.ett = EulerTourTree::new();
386        for &v in &vertices {
387            let _ = self.ett.make_tree(v);
388        }
389
390        // Re-apply all edges
391        let edges: Vec<(VertexId, VertexId)> = self.edges.iter().copied().collect();
392        for (u, v) in edges {
393            let root_u = self.find(u);
394            let root_v = self.find(v);
395
396            if root_u != root_v {
397                self.union(root_u, root_v);
398                // Link in ETT
399                let _ = self.ett.link(u, v);
400            }
401        }
402
403        // Mark ETT as synced
404        self.ett_synced = true;
405    }
406}
407
408impl Default for DynamicConnectivity {
409    fn default() -> Self {
410        Self::new()
411    }
412}
413
414#[cfg(test)]
415mod tests {
416    use super::*;
417
418    #[test]
419    fn test_new() {
420        let dc = DynamicConnectivity::new();
421        assert_eq!(dc.vertex_count(), 0);
422        assert_eq!(dc.component_count(), 0);
423    }
424
425    #[test]
426    fn test_add_vertex() {
427        let mut dc = DynamicConnectivity::new();
428
429        dc.add_vertex(0);
430        assert_eq!(dc.vertex_count(), 1);
431        assert_eq!(dc.component_count(), 1);
432
433        dc.add_vertex(1);
434        assert_eq!(dc.vertex_count(), 2);
435        assert_eq!(dc.component_count(), 2);
436
437        // Adding same vertex is no-op
438        dc.add_vertex(0);
439        assert_eq!(dc.vertex_count(), 2);
440        assert_eq!(dc.component_count(), 2);
441    }
442
443    #[test]
444    fn test_insert_edge_basic() {
445        let mut dc = DynamicConnectivity::new();
446
447        dc.insert_edge(0, 1);
448        assert_eq!(dc.vertex_count(), 2);
449        assert_eq!(dc.component_count(), 1);
450        assert!(dc.connected(0, 1));
451    }
452
453    #[test]
454    fn test_insert_edge_chain() {
455        let mut dc = DynamicConnectivity::new();
456
457        dc.insert_edge(0, 1);
458        dc.insert_edge(1, 2);
459        dc.insert_edge(2, 3);
460
461        assert_eq!(dc.vertex_count(), 4);
462        assert_eq!(dc.component_count(), 1);
463        assert!(dc.connected(0, 3));
464    }
465
466    #[test]
467    fn test_is_connected() {
468        let mut dc = DynamicConnectivity::new();
469
470        dc.add_vertex(0);
471        dc.add_vertex(1);
472        assert!(!dc.is_connected());
473
474        dc.insert_edge(0, 1);
475        assert!(dc.is_connected());
476
477        dc.add_vertex(2);
478        assert!(!dc.is_connected());
479
480        dc.insert_edge(1, 2);
481        assert!(dc.is_connected());
482    }
483
484    #[test]
485    fn test_delete_edge() {
486        let mut dc = DynamicConnectivity::new();
487
488        dc.insert_edge(0, 1);
489        dc.insert_edge(1, 2);
490        assert!(dc.connected(0, 2));
491
492        dc.delete_edge(1, 2);
493        assert!(dc.connected(0, 1));
494        assert!(!dc.connected(0, 2));
495        assert_eq!(dc.component_count(), 2);
496    }
497
498    #[test]
499    fn test_delete_edge_normalized() {
500        let mut dc = DynamicConnectivity::new();
501
502        dc.insert_edge(0, 1);
503        assert!(dc.connected(0, 1));
504
505        // Delete with reversed vertices
506        dc.delete_edge(1, 0);
507        assert!(!dc.connected(0, 1));
508    }
509
510    #[test]
511    fn test_multiple_components() {
512        let mut dc = DynamicConnectivity::new();
513
514        // Component 1: 0-1-2
515        dc.insert_edge(0, 1);
516        dc.insert_edge(1, 2);
517
518        // Component 2: 3-4
519        dc.insert_edge(3, 4);
520
521        // Isolated vertex
522        dc.add_vertex(5);
523
524        assert_eq!(dc.vertex_count(), 6);
525        assert_eq!(dc.component_count(), 3);
526
527        assert!(dc.connected(0, 2));
528        assert!(dc.connected(3, 4));
529        assert!(!dc.connected(0, 3));
530        assert!(!dc.connected(0, 5));
531    }
532
533    #[test]
534    fn test_path_compression() {
535        let mut dc = DynamicConnectivity::new();
536
537        // Create a long chain
538        for i in 0..10 {
539            dc.insert_edge(i, i + 1);
540        }
541
542        // Path compression should happen on find
543        assert!(dc.connected(0, 10));
544
545        // All vertices should now point closer to root
546        let root = dc.find(0);
547        for i in 0..=10 {
548            assert_eq!(dc.find(i), root);
549        }
550    }
551
552    #[test]
553    fn test_union_by_rank() {
554        let mut dc = DynamicConnectivity::new();
555
556        // Create two trees of different sizes
557        dc.insert_edge(0, 1);
558        dc.insert_edge(0, 2);
559        dc.insert_edge(0, 3);
560
561        dc.insert_edge(4, 5);
562
563        // Union them
564        dc.insert_edge(0, 4);
565
566        assert_eq!(dc.component_count(), 1);
567        assert!(dc.connected(1, 5));
568    }
569
570    #[test]
571    fn test_rebuild_after_multiple_deletions() {
572        let mut dc = DynamicConnectivity::new();
573
574        // Create a complete graph K4
575        dc.insert_edge(0, 1);
576        dc.insert_edge(0, 2);
577        dc.insert_edge(0, 3);
578        dc.insert_edge(1, 2);
579        dc.insert_edge(1, 3);
580        dc.insert_edge(2, 3);
581
582        assert!(dc.is_connected());
583
584        // Remove edges to disconnect
585        dc.delete_edge(0, 1);
586        dc.delete_edge(0, 2);
587        dc.delete_edge(0, 3);
588
589        assert!(!dc.is_connected());
590        assert_eq!(dc.component_count(), 2);
591        assert!(!dc.connected(0, 1));
592        assert!(dc.connected(1, 2));
593        assert!(dc.connected(1, 3));
594    }
595
596    #[test]
597    fn test_connected_nonexistent_vertex() {
598        let mut dc = DynamicConnectivity::new();
599
600        dc.add_vertex(0);
601        assert!(!dc.connected(0, 999));
602        assert!(!dc.connected(999, 0));
603    }
604
605    #[test]
606    fn test_self_loop() {
607        let mut dc = DynamicConnectivity::new();
608
609        dc.insert_edge(0, 0);
610        assert_eq!(dc.vertex_count(), 1);
611        assert_eq!(dc.component_count(), 1);
612        assert!(dc.connected(0, 0));
613    }
614
615    #[test]
616    fn test_duplicate_edges() {
617        let mut dc = DynamicConnectivity::new();
618
619        dc.insert_edge(0, 1);
620        dc.insert_edge(0, 1);  // Duplicate
621        dc.insert_edge(1, 0);  // Duplicate (reversed)
622
623        assert_eq!(dc.vertex_count(), 2);
624        assert_eq!(dc.component_count(), 1);
625    }
626}