1pub mod approximate;
14pub mod replacement;
15
16pub use replacement::{ReplacementEdgeIndex, ReplacementIndexStats};
17
18use crate::error::{MinCutError, Result};
19use crate::euler::EulerTourTree;
20use crate::graph::{DynamicGraph, Edge, EdgeId, VertexId, Weight};
21use crate::linkcut::LinkCutTree;
22use crate::tree::HierarchicalDecomposition;
23use parking_lot::RwLock;
24use std::sync::Arc;
25use std::time::Instant;
26
27#[derive(Debug, Clone)]
29pub struct MinCutConfig {
30 pub max_exact_cut_size: usize,
32 pub epsilon: f64,
34 pub approximate: bool,
36 pub parallel: bool,
38 pub cache_size: usize,
40}
41
42impl Default for MinCutConfig {
43 fn default() -> Self {
44 Self {
45 max_exact_cut_size: 1000,
46 epsilon: 0.1,
47 approximate: false,
48 parallel: true,
49 cache_size: 10000,
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct MinCutResult {
57 pub value: f64,
59 pub cut_edges: Option<Vec<Edge>>,
61 pub partition: Option<(Vec<VertexId>, Vec<VertexId>)>,
63 pub is_exact: bool,
65 pub approximation_ratio: f64,
67}
68
69#[derive(Debug, Clone, Default)]
71pub struct AlgorithmStats {
72 pub insertions: u64,
74 pub deletions: u64,
76 pub queries: u64,
78 pub avg_update_time_us: f64,
80 pub avg_query_time_us: f64,
82 pub restructures: u64,
84}
85
86pub struct DynamicMinCut {
88 graph: Arc<RwLock<DynamicGraph>>,
90 decomposition: HierarchicalDecomposition,
92 link_cut_tree: LinkCutTree,
94 spanning_forest: EulerTourTree,
96 current_min_cut: f64,
98 config: MinCutConfig,
100 stats: Arc<RwLock<AlgorithmStats>>,
102 tree_edges: Arc<RwLock<std::collections::HashSet<(VertexId, VertexId)>>>,
104}
105
106impl DynamicMinCut {
107 pub fn new(config: MinCutConfig) -> Self {
109 let empty_graph = Arc::new(DynamicGraph::new());
110 Self {
111 graph: Arc::new(RwLock::new(DynamicGraph::new())),
112 decomposition: HierarchicalDecomposition::build(empty_graph).unwrap(),
113 link_cut_tree: LinkCutTree::new(),
114 spanning_forest: EulerTourTree::new(),
115 current_min_cut: f64::INFINITY,
116 config,
117 stats: Arc::new(RwLock::new(AlgorithmStats::default())),
118 tree_edges: Arc::new(RwLock::new(std::collections::HashSet::new())),
119 }
120 }
121
122 pub fn from_graph(graph: DynamicGraph, config: MinCutConfig) -> Result<Self> {
124 let graph_shared = Arc::new(RwLock::new(graph.clone()));
126
127 let mut link_cut_tree = LinkCutTree::new();
129 let mut spanning_forest = EulerTourTree::new();
130
131 let vertices = graph.vertices();
132 for &v in &vertices {
133 link_cut_tree.make_tree(v, 0.0);
134 spanning_forest.make_tree(v)?;
135 }
136
137 let mut visited = std::collections::HashSet::new();
139 let mut tree_edges = std::collections::HashSet::new();
140
141 for &start_vertex in &vertices {
142 if visited.contains(&start_vertex) {
143 continue;
144 }
145
146 let mut stack = vec![start_vertex];
148 visited.insert(start_vertex);
149
150 while let Some(u) = stack.pop() {
151 let neighbors = graph.neighbors(u);
152 for (v, _) in neighbors {
153 if !visited.contains(&v) {
154 visited.insert(v);
155 stack.push(v);
156
157 link_cut_tree.link(u, v)?;
159 spanning_forest.link(u, v)?;
160
161 let key = if u < v { (u, v) } else { (v, u) };
163 tree_edges.insert(key);
164 }
165 }
166 }
167 }
168
169 let graph_for_decomp = Arc::new(graph);
171 let decomposition = HierarchicalDecomposition::build(graph_for_decomp)?;
172
173 let mut mincut = Self {
175 graph: graph_shared,
176 decomposition,
177 link_cut_tree,
178 spanning_forest,
179 current_min_cut: f64::INFINITY,
180 config,
181 stats: Arc::new(RwLock::new(AlgorithmStats::default())),
182 tree_edges: Arc::new(RwLock::new(tree_edges)),
183 };
184
185 mincut.recompute_min_cut();
187
188 Ok(mincut)
189 }
190
191 pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) -> Result<f64> {
193 let start_time = Instant::now();
194
195 {
197 let graph = self.graph.write();
198 graph.insert_edge(u, v, weight)?;
199 }
200
201 let u_exists = self.link_cut_tree.len() > 0 && self.link_cut_tree.find_root(u).is_ok();
204 let v_exists = self.link_cut_tree.len() > 0 && self.link_cut_tree.find_root(v).is_ok();
205
206 if !u_exists {
207 self.link_cut_tree.make_tree(u, 0.0);
208 self.spanning_forest.make_tree(u)?;
209 }
210 if !v_exists {
211 self.link_cut_tree.make_tree(v, 0.0);
212 self.spanning_forest.make_tree(v)?;
213 }
214
215 let connected = self.link_cut_tree.connected(u, v);
217
218 if !connected {
219 self.handle_bridge_edge(u, v, weight)?;
221 } else {
222 self.handle_cycle_edge(u, v, weight)?;
224 }
225
226 self.rebuild_decomposition();
228
229 self.recompute_min_cut();
231
232 let elapsed = start_time.elapsed().as_micros() as f64;
234 let mut stats = self.stats.write();
235 stats.insertions += 1;
236 let n = stats.insertions as f64;
237 stats.avg_update_time_us = (stats.avg_update_time_us * (n - 1.0) + elapsed) / n;
238 drop(stats);
239
240 Ok(self.current_min_cut)
241 }
242
243 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> Result<f64> {
245 let start_time = Instant::now();
246
247 {
249 let graph = self.graph.write();
250 graph.delete_edge(u, v)?;
251 }
252
253 let key = if u < v { (u, v) } else { (v, u) };
255 let is_tree_edge = self.tree_edges.read().contains(&key);
256
257 if is_tree_edge {
258 self.handle_tree_edge_deletion(u, v)?;
259 } else {
260 self.handle_non_tree_edge_deletion(u, v)?;
261 }
262
263 self.rebuild_decomposition();
265
266 self.recompute_min_cut();
268
269 let elapsed = start_time.elapsed().as_micros() as f64;
271 let mut stats = self.stats.write();
272 stats.deletions += 1;
273 let n = stats.deletions as f64;
274 stats.avg_update_time_us = (stats.avg_update_time_us * (n - 1.0) + elapsed) / n;
275 drop(stats);
276
277 Ok(self.current_min_cut)
278 }
279
280 pub fn min_cut_value(&self) -> f64 {
282 let start_time = Instant::now();
283
284 let value = self.current_min_cut;
285
286 let elapsed = start_time.elapsed().as_micros() as f64;
288 let mut stats = self.stats.write();
289 stats.queries += 1;
290 let n = stats.queries as f64;
291 stats.avg_query_time_us = (stats.avg_query_time_us * (n - 1.0) + elapsed) / n;
292
293 value
294 }
295
296 pub fn min_cut(&self) -> MinCutResult {
298 let value = self.min_cut_value();
299 let (partition_s, partition_t) = self.partition();
300 let edges = self.cut_edges();
301
302 MinCutResult {
303 value,
304 cut_edges: Some(edges),
305 partition: Some((partition_s, partition_t)),
306 is_exact: !self.config.approximate,
307 approximation_ratio: if self.config.approximate {
308 1.0 + self.config.epsilon
309 } else {
310 1.0
311 },
312 }
313 }
314
315 pub fn partition(&self) -> (Vec<VertexId>, Vec<VertexId>) {
317 let (set_a, set_b) = self.decomposition.min_cut_partition();
319 (set_a.into_iter().collect(), set_b.into_iter().collect())
320 }
321
322 pub fn cut_edges(&self) -> Vec<Edge> {
324 let (partition_s, partition_t) = self.partition();
325 let partition_t_set: std::collections::HashSet<_> = partition_t.iter().copied().collect();
326
327 let graph = self.graph.read();
328 let mut cut_edges = Vec::new();
329
330 for &u in &partition_s {
331 let neighbors = graph.neighbors(u);
332 for (v, _) in neighbors {
333 if partition_t_set.contains(&v) {
334 if let Some(edge) = graph.get_edge(u, v) {
335 cut_edges.push(edge);
336 }
337 }
338 }
339 }
340
341 cut_edges
342 }
343
344 pub fn is_connected(&self) -> bool {
346 let graph = self.graph.read();
347 graph.is_connected()
348 }
349
350 pub fn stats(&self) -> AlgorithmStats {
352 self.stats.read().clone()
353 }
354
355 pub fn reset_stats(&mut self) {
357 *self.stats.write() = AlgorithmStats::default();
358 }
359
360 pub fn config(&self) -> &MinCutConfig {
362 &self.config
363 }
364
365 pub fn graph(&self) -> Arc<RwLock<DynamicGraph>> {
367 Arc::clone(&self.graph)
368 }
369
370 pub fn num_vertices(&self) -> usize {
372 self.graph.read().num_vertices()
373 }
374
375 pub fn num_edges(&self) -> usize {
377 self.graph.read().num_edges()
378 }
379
380 fn handle_cycle_edge(&mut self, _u: VertexId, _v: VertexId, _weight: Weight) -> Result<()> {
384 Ok(())
387 }
388
389 fn handle_bridge_edge(&mut self, u: VertexId, v: VertexId, _weight: Weight) -> Result<()> {
391 self.link_cut_tree.link(u, v)?;
393 self.spanning_forest.link(u, v)?;
394
395 let key = if u < v { (u, v) } else { (v, u) };
397 self.tree_edges.write().insert(key);
398
399 Ok(())
400 }
401
402 fn handle_tree_edge_deletion(&mut self, u: VertexId, v: VertexId) -> Result<()> {
404 let key = if u < v { (u, v) } else { (v, u) };
406 self.tree_edges.write().remove(&key);
407
408 if self.link_cut_tree.connected(u, v) {
411 if self.link_cut_tree.cut(u).is_err() {
414 let _ = self.link_cut_tree.cut(v); }
416 }
417
418 if let Some((x, y)) = self.find_replacement_edge(u, v) {
420 let already_connected = self.link_cut_tree.connected(x, y);
422
423 if !already_connected {
424 self.link_cut_tree.link(x, y)?;
426
427 if !self.spanning_forest.connected(x, y) {
429 let _ = self.spanning_forest.link(x, y); }
431
432 let key = if x < y { (x, y) } else { (y, x) };
434 self.tree_edges.write().insert(key);
435 }
436 } else {
437 self.stats.write().restructures += 1;
440 }
441
442 Ok(())
443 }
444
445 fn handle_non_tree_edge_deletion(&mut self, _u: VertexId, _v: VertexId) -> Result<()> {
447 Ok(())
450 }
451
452 fn find_replacement_edge(&mut self, u: VertexId, v: VertexId) -> Option<(VertexId, VertexId)> {
454 let graph = self.graph.read();
457
458 let mut comp_u = std::collections::HashSet::new();
459 let mut queue = std::collections::VecDeque::new();
460
461 queue.push_back(u);
462 comp_u.insert(u);
463
464 while let Some(x) = queue.pop_front() {
465 let neighbors = graph.neighbors(x);
466 for (y, _) in neighbors {
467 if (x == u && y == v) || (x == v && y == u) {
469 continue;
470 }
471
472 let key = if x < y { (x, y) } else { (y, x) };
474 if !self.tree_edges.read().contains(&key) {
475 continue;
476 }
477
478 if comp_u.insert(y) {
479 queue.push_back(y);
480 }
481 }
482 }
483
484 for &x in &comp_u {
486 let neighbors = graph.neighbors(x);
487 for (y, _) in neighbors {
488 if !comp_u.contains(&y) {
489 return Some((x, y));
491 }
492 }
493 }
494
495 None
496 }
497
498 fn rebuild_decomposition(&mut self) {
500 let graph = self.graph.read();
501 let graph_clone = graph.clone();
502 drop(graph);
503
504 let graph_for_decomp = Arc::new(graph_clone);
505 self.decomposition =
506 HierarchicalDecomposition::build(graph_for_decomp).unwrap_or_else(|_| {
507 let empty = Arc::new(DynamicGraph::new());
509 HierarchicalDecomposition::build(empty).unwrap()
510 });
511 }
512
513 fn recompute_min_cut(&mut self) {
515 let graph = self.graph.read();
516
517 if !graph.is_connected() {
519 self.current_min_cut = 0.0;
520 drop(graph);
521 return;
522 }
523
524 let mut min_cut = self.decomposition.min_cut_value();
526
527 let tree_edges: Vec<_> = self.tree_edges.read().iter().copied().collect();
530
531 for (u, v) in tree_edges {
532 let cut_value = self.compute_tree_edge_cut(&graph, u, v);
533 if cut_value < min_cut {
534 min_cut = cut_value;
535 }
536 }
537
538 drop(graph);
539 self.current_min_cut = min_cut;
540 }
541
542 fn compute_tree_edge_cut(&self, graph: &DynamicGraph, u: VertexId, v: VertexId) -> f64 {
544 let mut component_u = std::collections::HashSet::new();
546 let mut queue = std::collections::VecDeque::new();
547
548 queue.push_back(u);
549 component_u.insert(u);
550
551 while let Some(x) = queue.pop_front() {
552 for (y, _) in graph.neighbors(x) {
553 if (x == u && y == v) || (x == v && y == u) {
555 continue;
556 }
557
558 let key = if x < y { (x, y) } else { (y, x) };
560 if !self.tree_edges.read().contains(&key) {
561 continue;
562 }
563
564 if component_u.insert(y) {
565 queue.push_back(y);
566 }
567 }
568 }
569
570 let mut cut_weight = 0.0;
572 for &x in &component_u {
573 for (y, _) in graph.neighbors(x) {
574 if !component_u.contains(&y) {
575 if let Some(weight) = graph.edge_weight(x, y) {
576 cut_weight += weight;
577 }
578 }
579 }
580 }
581
582 cut_weight
583 }
584}
585
586pub struct MinCutBuilder {
588 config: MinCutConfig,
589 initial_edges: Vec<(VertexId, VertexId, Weight)>,
590}
591
592impl MinCutBuilder {
593 pub fn new() -> Self {
595 Self {
596 config: MinCutConfig::default(),
597 initial_edges: Vec::new(),
598 }
599 }
600
601 pub fn exact(mut self) -> Self {
603 self.config.approximate = false;
604 self
605 }
606
607 pub fn approximate(mut self, epsilon: f64) -> Self {
609 assert!(epsilon > 0.0 && epsilon <= 1.0, "Epsilon must be in (0, 1]");
610 self.config.approximate = true;
611 self.config.epsilon = epsilon;
612 self
613 }
614
615 pub fn max_cut_size(mut self, size: usize) -> Self {
617 self.config.max_exact_cut_size = size;
618 self
619 }
620
621 pub fn parallel(mut self, enabled: bool) -> Self {
623 self.config.parallel = enabled;
624 self
625 }
626
627 pub fn with_edges(mut self, edges: Vec<(VertexId, VertexId, Weight)>) -> Self {
629 self.initial_edges = edges;
630 self
631 }
632
633 pub fn build(self) -> Result<DynamicMinCut> {
635 if self.initial_edges.is_empty() {
636 Ok(DynamicMinCut::new(self.config))
637 } else {
638 let graph = DynamicGraph::new();
640 for (u, v, weight) in &self.initial_edges {
641 graph.insert_edge(*u, *v, *weight)?;
642 }
643
644 DynamicMinCut::from_graph(graph, self.config)
645 }
646 }
647}
648
649impl Default for MinCutBuilder {
650 fn default() -> Self {
651 Self::new()
652 }
653}
654
655#[cfg(test)]
656mod tests {
657 use super::*;
658
659 #[test]
660 fn test_empty_graph() {
661 let mincut = DynamicMinCut::new(MinCutConfig::default());
662 assert_eq!(mincut.min_cut_value(), f64::INFINITY);
663 assert_eq!(mincut.num_vertices(), 0);
664 assert_eq!(mincut.num_edges(), 0);
665 }
666
667 #[test]
668 fn test_single_edge() {
669 let mincut = MinCutBuilder::new()
670 .with_edges(vec![(1, 2, 1.0)])
671 .build()
672 .unwrap();
673
674 assert_eq!(mincut.num_vertices(), 2);
675 assert_eq!(mincut.num_edges(), 1);
676 assert_eq!(mincut.min_cut_value(), 1.0);
677 assert!(mincut.is_connected());
678 }
679
680 #[test]
681 fn test_triangle() {
682 let edges = vec![(1, 2, 1.0), (2, 3, 1.0), (3, 1, 1.0)];
683
684 let mincut = MinCutBuilder::new().with_edges(edges).build().unwrap();
685
686 assert_eq!(mincut.num_vertices(), 3);
687 assert_eq!(mincut.num_edges(), 3);
688 assert_eq!(mincut.min_cut_value(), 2.0); assert!(mincut.is_connected());
690 }
691
692 #[test]
693 fn test_insert_edge() {
694 let mut mincut = MinCutBuilder::new()
695 .with_edges(vec![(1, 2, 1.0)])
696 .build()
697 .unwrap();
698
699 let cut_value = mincut.insert_edge(2, 3, 1.0).unwrap();
700 assert_eq!(mincut.num_edges(), 2);
701 assert_eq!(cut_value, 1.0);
702 }
703
704 #[test]
705 fn test_delete_edge() {
706 let mut mincut = MinCutBuilder::new()
707 .with_edges(vec![(1, 2, 1.0), (2, 3, 1.0)])
708 .build()
709 .unwrap();
710
711 assert!(mincut.is_connected());
712
713 let cut_value = mincut.delete_edge(1, 2).unwrap();
714 assert_eq!(mincut.num_edges(), 1);
715 assert_eq!(cut_value, 0.0);
717 }
718
719 #[test]
720 fn test_disconnected_graph() {
721 let mincut = MinCutBuilder::new()
722 .with_edges(vec![(1, 2, 1.0), (3, 4, 1.0)])
723 .build()
724 .unwrap();
725
726 assert!(!mincut.is_connected());
727 assert_eq!(mincut.min_cut_value(), 0.0);
728 }
729
730 #[test]
731 fn test_weighted_edges() {
732 let edges = vec![(1, 2, 2.0), (2, 3, 3.0), (3, 1, 1.0)];
733
734 let mincut = MinCutBuilder::new().with_edges(edges).build().unwrap();
735
736 assert_eq!(mincut.min_cut_value(), 3.0);
738 }
739
740 #[test]
741 fn test_partition() {
742 let edges = vec![(1, 2, 1.0), (2, 3, 1.0), (3, 4, 1.0)];
743
744 let mincut = MinCutBuilder::new().with_edges(edges).build().unwrap();
745
746 let (s, t) = mincut.partition();
747 assert!(!s.is_empty());
748 assert!(!t.is_empty());
749 assert_eq!(s.len() + t.len(), 4);
750 }
751
752 #[test]
753 fn test_cut_edges() {
754 let edges = vec![(1, 2, 1.0), (2, 3, 1.0)];
755
756 let mincut = MinCutBuilder::new().with_edges(edges).build().unwrap();
757
758 let cut = mincut.cut_edges();
759 assert!(!cut.is_empty());
760 assert!(cut.len() <= 2);
761 }
762
763 #[test]
764 fn test_min_cut_result() {
765 let edges = vec![(1, 2, 1.0), (2, 3, 1.0), (3, 1, 1.0)];
766
767 let mincut = MinCutBuilder::new()
768 .exact()
769 .with_edges(edges)
770 .build()
771 .unwrap();
772
773 let result = mincut.min_cut();
774 assert!(result.is_exact);
775 assert_eq!(result.approximation_ratio, 1.0);
776 assert!(result.cut_edges.is_some());
777 assert!(result.partition.is_some());
778 }
779
780 #[test]
781 fn test_approximate_mode() {
782 let mincut = MinCutBuilder::new().approximate(0.1).build().unwrap();
783
784 let result = mincut.min_cut();
785 assert!(!result.is_exact);
786 assert_eq!(result.approximation_ratio, 1.1);
787 }
788
789 #[test]
790 fn test_statistics() {
791 let mut mincut = MinCutBuilder::new()
792 .with_edges(vec![(1, 2, 1.0)])
793 .build()
794 .unwrap();
795
796 mincut.insert_edge(2, 3, 1.0).unwrap();
797 mincut.delete_edge(1, 2).unwrap();
798 let _ = mincut.min_cut_value();
799
800 let stats = mincut.stats();
801 assert_eq!(stats.insertions, 1);
802 assert_eq!(stats.deletions, 1);
803 assert_eq!(stats.queries, 1);
804 assert!(stats.avg_update_time_us > 0.0);
805 }
806
807 #[test]
808 fn test_reset_stats() {
809 let mut mincut = MinCutBuilder::new()
810 .with_edges(vec![(1, 2, 1.0)])
811 .build()
812 .unwrap();
813
814 mincut.insert_edge(2, 3, 1.0).unwrap();
815 assert_eq!(mincut.stats().insertions, 1);
816
817 mincut.reset_stats();
818 assert_eq!(mincut.stats().insertions, 0);
819 }
820
821 #[test]
822 fn test_builder_pattern() {
823 let mincut = MinCutBuilder::new()
824 .exact()
825 .max_cut_size(500)
826 .parallel(true)
827 .with_edges(vec![(1, 2, 1.0)])
828 .build()
829 .unwrap();
830
831 assert!(!mincut.config().approximate);
832 assert_eq!(mincut.config().max_exact_cut_size, 500);
833 assert!(mincut.config().parallel);
834 }
835
836 #[test]
837 fn test_large_graph() {
838 let mut edges = Vec::new();
839
840 for i in 0..99 {
842 edges.push((i, i + 1, 1.0));
843 }
844
845 let mincut = MinCutBuilder::new().with_edges(edges).build().unwrap();
846
847 assert_eq!(mincut.num_vertices(), 100);
848 assert_eq!(mincut.num_edges(), 99);
849 assert_eq!(mincut.min_cut_value(), 1.0); assert!(mincut.is_connected());
851 }
852
853 #[test]
854 fn test_tree_edge_deletion_with_replacement() {
855 let mut mincut = MinCutBuilder::new()
856 .with_edges(vec![
857 (1, 2, 1.0),
858 (2, 3, 1.0),
859 (1, 3, 1.0), ])
861 .build()
862 .unwrap();
863
864 assert!(mincut.is_connected());
865
866 mincut.delete_edge(1, 2).unwrap();
868
869 assert_eq!(mincut.num_edges(), 2);
871 }
872
873 #[test]
874 fn test_multiple_components() {
875 let edges = vec![(1, 2, 1.0), (3, 4, 1.0), (5, 6, 1.0)];
876
877 let mincut = MinCutBuilder::new().with_edges(edges).build().unwrap();
878
879 assert!(!mincut.is_connected());
880 assert_eq!(mincut.min_cut_value(), 0.0);
881 }
882
883 #[test]
884 fn test_dynamic_updates() {
885 let mut mincut = MinCutBuilder::new().build().unwrap();
886
887 assert_eq!(mincut.min_cut_value(), f64::INFINITY);
889
890 mincut.insert_edge(1, 2, 2.0).unwrap();
892 assert_eq!(mincut.min_cut_value(), 2.0);
893
894 mincut.insert_edge(2, 3, 3.0).unwrap();
896 mincut.insert_edge(3, 1, 1.0).unwrap();
897 assert_eq!(mincut.min_cut_value(), 3.0); mincut.delete_edge(2, 3).unwrap();
901 assert_eq!(mincut.min_cut_value(), 1.0); }
903
904 #[test]
905 fn test_config_access() {
906 let mincut = MinCutBuilder::new()
907 .approximate(0.2)
908 .max_cut_size(2000)
909 .build()
910 .unwrap();
911
912 let config = mincut.config();
913 assert_eq!(config.epsilon, 0.2);
914 assert_eq!(config.max_exact_cut_size, 2000);
915 assert!(config.approximate);
916 }
917
918 #[test]
919 fn test_graph_access() {
920 let mincut = MinCutBuilder::new()
921 .with_edges(vec![(1, 2, 1.0)])
922 .build()
923 .unwrap();
924
925 let graph = mincut.graph();
926 let g = graph.read();
927 assert_eq!(g.num_vertices(), 2);
928 assert_eq!(g.num_edges(), 1);
929 }
930
931 #[test]
932 fn test_bridge_graph() {
933 let edges = vec![
935 (1, 2, 2.0),
937 (2, 3, 2.0),
938 (3, 1, 2.0),
939 (3, 4, 1.0),
941 (4, 5, 2.0),
943 (5, 6, 2.0),
944 (6, 4, 2.0),
945 ];
946
947 let mincut = MinCutBuilder::new().with_edges(edges).build().unwrap();
948
949 assert_eq!(mincut.min_cut_value(), 1.0);
951 assert!(mincut.is_connected());
952 }
953
954 #[test]
955 fn test_complete_graph_k4() {
956 let mut edges = Vec::new();
958 for i in 1..=4 {
959 for j in (i + 1)..=4 {
960 edges.push((i, j, 1.0));
961 }
962 }
963
964 let mincut = MinCutBuilder::new().with_edges(edges).build().unwrap();
965
966 assert_eq!(mincut.num_vertices(), 4);
967 assert_eq!(mincut.num_edges(), 6);
968 assert_eq!(mincut.min_cut_value(), 3.0);
970 }
971
972 #[test]
973 fn test_sequential_insertions() {
974 let mut mincut = MinCutBuilder::new().build().unwrap();
975
976 mincut.insert_edge(1, 2, 1.0).unwrap();
978 assert_eq!(mincut.min_cut_value(), 1.0);
979
980 mincut.insert_edge(2, 3, 1.0).unwrap();
981 assert_eq!(mincut.min_cut_value(), 1.0);
982
983 mincut.insert_edge(3, 4, 1.0).unwrap();
984 assert_eq!(mincut.min_cut_value(), 1.0);
985
986 mincut.insert_edge(4, 1, 1.0).unwrap();
988 assert_eq!(mincut.min_cut_value(), 2.0);
989 }
990
991 #[test]
992 fn test_sequential_deletions() {
993 let mut mincut = MinCutBuilder::new()
994 .with_edges(vec![(1, 2, 1.0), (2, 3, 1.0), (3, 1, 1.0)])
995 .build()
996 .unwrap();
997
998 assert_eq!(mincut.min_cut_value(), 2.0);
999
1000 mincut.delete_edge(1, 2).unwrap();
1002 assert_eq!(mincut.min_cut_value(), 1.0);
1003
1004 mincut.delete_edge(2, 3).unwrap();
1006 assert_eq!(mincut.min_cut_value(), 0.0);
1007 }
1008}