1#![allow(clippy::cast_possible_truncation)]
49
50use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
51
52pub fn max_flow_value(
99 graph: &Graph,
100 source: VertexId,
101 target: VertexId,
102 capacity: Option<&[f64]>,
103) -> IgraphResult<f64> {
104 max_flow_with_residual(graph, source, target, capacity).map(|(v, _)| v)
105}
106
107pub fn max_flow(
167 graph: &Graph,
168 source: VertexId,
169 target: VertexId,
170 capacity: Option<&[f64]>,
171) -> IgraphResult<MaxFlow> {
172 let m = graph.ecount();
173 let directed = graph.is_directed();
174 let (value, net) = max_flow_with_residual(graph, source, target, capacity)?;
175
176 let mut flow_vec: Vec<f64> = Vec::with_capacity(m);
192 for e in 0..m {
193 let fwd = 2 * e;
194 let rev = fwd + 1;
195 if directed {
196 let initial = capacity.map_or(1.0, |c| c[e]);
197 flow_vec.push(initial - net.cap[fwd]);
198 } else {
199 flow_vec.push((net.cap[rev] - net.cap[fwd]) / 2.0);
200 }
201 }
202
203 let n = net.n;
205 let mut in_source = vec![false; n];
206 in_source[source as usize] = true;
207 let mut queue: Vec<u32> = Vec::with_capacity(n);
208 queue.push(source);
209 let mut head_ptr = 0_usize;
210 while head_ptr < queue.len() {
211 let v = queue[head_ptr] as usize;
212 head_ptr += 1;
213 for &arc in &net.arcs_out[v] {
214 let arc_us = arc as usize;
215 if net.cap[arc_us] <= 0.0 {
216 continue;
217 }
218 let w = net.head[arc_us] as usize;
219 if !in_source[w] {
220 in_source[w] = true;
221 queue.push(w as u32);
222 }
223 }
224 }
225
226 let mut partition: Vec<u32> = Vec::with_capacity(queue.len());
227 let mut partition2: Vec<u32> = Vec::with_capacity(n.saturating_sub(queue.len()));
228 for (v, &is_src) in in_source.iter().enumerate() {
229 if is_src {
230 partition.push(v as u32);
231 } else {
232 partition2.push(v as u32);
233 }
234 }
235
236 let edge_count = u32::try_from(m).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
238 let mut cut: Vec<u32> = Vec::new();
239 for eid in 0..edge_count {
240 let (u, v) = graph.edge(eid)?;
241 let u_in = in_source[u as usize];
242 let v_in = in_source[v as usize];
243 let crosses = if directed {
244 u_in && !v_in
245 } else {
246 u_in != v_in
247 };
248 if crosses {
249 cut.push(eid);
250 }
251 }
252
253 Ok(MaxFlow {
254 value,
255 flow: flow_vec,
256 cut,
257 partition,
258 partition2,
259 })
260}
261
262#[derive(Debug, Clone)]
269pub struct MaxFlow {
270 pub value: f64,
272 pub flow: Vec<f64>,
277 pub cut: Vec<u32>,
280 pub partition: Vec<u32>,
283 pub partition2: Vec<u32>,
286}
287
288pub(crate) fn max_flow_with_residual(
296 graph: &Graph,
297 source: VertexId,
298 target: VertexId,
299 capacity: Option<&[f64]>,
300) -> IgraphResult<(f64, Network)> {
301 let n = graph.vcount();
302 if n == 0 || source >= n {
303 return Err(IgraphError::VertexOutOfRange { id: source, n });
304 }
305 if target >= n {
306 return Err(IgraphError::VertexOutOfRange { id: target, n });
307 }
308 if source == target {
309 return Err(IgraphError::InvalidArgument(
310 "source and target must be distinct".to_string(),
311 ));
312 }
313
314 let m = graph.ecount();
315 if let Some(c) = capacity {
316 if c.len() != m {
317 return Err(IgraphError::InvalidArgument(format!(
318 "capacity length {} does not match edge count {}",
319 c.len(),
320 m
321 )));
322 }
323 for (i, &v) in c.iter().enumerate() {
324 if !v.is_finite() || v < 0.0 {
325 return Err(IgraphError::InvalidArgument(format!(
326 "capacity[{i}] = {v} is not a finite non-negative number"
327 )));
328 }
329 }
330 }
331
332 let net = Network::build(graph, capacity)?;
333 let mut state = DinicState::new(net);
334 let value = state.run(source, target);
335 Ok((value, state.into_network()))
336}
337
338pub(crate) struct Network {
349 pub(crate) n: usize,
350 pub(crate) head: Vec<u32>,
352 pub(crate) cap: Vec<f64>,
358 pub(crate) arcs_out: Vec<Vec<u32>>,
361}
362
363impl Network {
364 fn build(graph: &Graph, capacity: Option<&[f64]>) -> IgraphResult<Self> {
365 let n = graph.vcount() as usize;
366 let m = graph.ecount();
367 let directed = graph.is_directed();
368
369 let arc_count = m
374 .checked_mul(2)
375 .ok_or(IgraphError::Internal("arc count overflows usize"))?;
376
377 let mut head = vec![0_u32; arc_count];
378 let mut cap = vec![0.0_f64; arc_count];
379 let mut arcs_out: Vec<Vec<u32>> = vec![Vec::new(); n];
380
381 let edge_count =
382 u32::try_from(m).map_err(|_| IgraphError::Internal("ecount overflows u32"))?;
383 for eid in 0..edge_count {
384 let (src, dst) = graph.edge(eid)?;
385 let e_us = eid as usize;
386 let cap_val = capacity.map_or(1.0, |c| c[e_us]);
387
388 let fwd = 2 * e_us;
389 let rev = fwd + 1;
390 head[fwd] = dst;
391 head[rev] = src;
392 cap[fwd] = cap_val;
393 cap[rev] = if directed { 0.0 } else { cap_val };
394 arcs_out[src as usize].push(fwd as u32);
398 arcs_out[dst as usize].push(rev as u32);
399 }
400
401 Ok(Self {
402 n,
403 head,
404 cap,
405 arcs_out,
406 })
407 }
408}
409
410struct DinicState {
411 net: Network,
412 level: Vec<u32>,
414 iter: Vec<u32>,
417 queue: Vec<u32>,
419}
420
421impl DinicState {
422 fn new(net: Network) -> Self {
423 let n = net.n;
424 Self {
425 net,
426 level: vec![u32::MAX; n],
427 iter: vec![0_u32; n],
428 queue: Vec::with_capacity(n),
429 }
430 }
431
432 fn into_network(self) -> Network {
435 self.net
436 }
437
438 fn run(&mut self, source: u32, target: u32) -> f64 {
439 let mut total = 0.0_f64;
440 let src = source as usize;
441 let dst = target as usize;
442 while self.bfs(src, dst) {
443 for it in &mut self.iter {
444 *it = 0;
445 }
446 loop {
447 let pushed = self.dfs(src, dst, f64::INFINITY);
448 if pushed <= 0.0 {
449 break;
450 }
451 total += pushed;
452 }
453 }
454 total
455 }
456
457 fn bfs(&mut self, source: usize, target: usize) -> bool {
460 for l in &mut self.level {
461 *l = u32::MAX;
462 }
463 self.level[source] = 0;
464 self.queue.clear();
465 self.queue.push(source as u32);
466 let mut head_ptr = 0_usize;
467 while head_ptr < self.queue.len() {
468 let v = self.queue[head_ptr] as usize;
469 head_ptr += 1;
470 let next_level = self.level[v].saturating_add(1);
471 for &a in &self.net.arcs_out[v] {
472 let a_us = a as usize;
473 if self.net.cap[a_us] <= 0.0 {
474 continue;
475 }
476 let w = self.net.head[a_us] as usize;
477 if self.level[w] == u32::MAX {
478 self.level[w] = next_level;
479 self.queue.push(w as u32);
480 }
481 }
482 }
483 self.level[target] != u32::MAX
484 }
485
486 fn dfs(&mut self, v: usize, target: usize, limit: f64) -> f64 {
490 if v == target {
491 return limit;
492 }
493 let next_level = self.level[v].saturating_add(1);
494 while (self.iter[v] as usize) < self.net.arcs_out[v].len() {
495 let arc_idx = self.net.arcs_out[v][self.iter[v] as usize] as usize;
496 let w = self.net.head[arc_idx] as usize;
497 let cap_here = self.net.cap[arc_idx];
498 if cap_here > 0.0 && self.level[w] == next_level {
499 let send = limit.min(cap_here);
500 let pushed = self.dfs(w, target, send);
501 if pushed > 0.0 {
502 self.net.cap[arc_idx] -= pushed;
503 self.net.cap[arc_idx ^ 1] += pushed;
504 return pushed;
505 }
506 }
507 self.iter[v] += 1;
508 }
509 0.0
510 }
511}
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516
517 fn assert_close(actual: f64, expected: f64, tol: f64) {
518 assert!(
519 (actual - expected).abs() < tol,
520 "actual = {actual}, expected = {expected}"
521 );
522 }
523
524 #[test]
525 fn rejects_out_of_range_source() {
526 let g = Graph::with_vertices(2);
527 let err = max_flow_value(&g, 5, 1, None).unwrap_err();
528 match err {
529 IgraphError::VertexOutOfRange { id, n } => {
530 assert_eq!(id, 5);
531 assert_eq!(n, 2);
532 }
533 _ => panic!("expected VertexOutOfRange"),
534 }
535 }
536
537 #[test]
538 fn rejects_out_of_range_target() {
539 let g = Graph::with_vertices(2);
540 let err = max_flow_value(&g, 0, 9, None).unwrap_err();
541 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 9, n: 2 }));
542 }
543
544 #[test]
545 fn rejects_source_equals_target() {
546 let g = Graph::with_vertices(2);
547 let err = max_flow_value(&g, 0, 0, None).unwrap_err();
548 assert!(matches!(err, IgraphError::InvalidArgument(_)));
549 }
550
551 #[test]
552 fn rejects_wrong_capacity_length() {
553 let mut g = Graph::with_vertices(2);
554 g.add_edge(0, 1).unwrap();
555 let cap = vec![1.0, 2.0];
556 let err = max_flow_value(&g, 0, 1, Some(&cap)).unwrap_err();
557 assert!(matches!(err, IgraphError::InvalidArgument(_)));
558 }
559
560 #[test]
561 fn rejects_negative_capacity() {
562 let mut g = Graph::with_vertices(2);
563 g.add_edge(0, 1).unwrap();
564 let cap = vec![-1.0];
565 let err = max_flow_value(&g, 0, 1, Some(&cap)).unwrap_err();
566 assert!(matches!(err, IgraphError::InvalidArgument(_)));
567 }
568
569 #[test]
570 fn isolated_source_and_target() {
571 let g = Graph::with_vertices(2);
572 let f = max_flow_value(&g, 0, 1, None).unwrap();
573 assert_close(f, 0.0, 1e-12);
574 }
575
576 #[test]
577 fn single_edge_directed_unit() {
578 let mut g = Graph::new(2, true).unwrap();
579 g.add_edge(0, 1).unwrap();
580 let f = max_flow_value(&g, 0, 1, None).unwrap();
581 assert_close(f, 1.0, 1e-12);
582 }
583
584 #[test]
585 fn single_edge_directed_wrong_direction() {
586 let mut g = Graph::new(2, true).unwrap();
587 g.add_edge(0, 1).unwrap();
588 let f = max_flow_value(&g, 1, 0, None).unwrap();
589 assert_close(f, 0.0, 1e-12);
590 }
591
592 #[test]
593 fn single_edge_undirected_unit() {
594 let mut g = Graph::with_vertices(2);
595 g.add_edge(0, 1).unwrap();
596 let f = max_flow_value(&g, 0, 1, None).unwrap();
597 assert_close(f, 1.0, 1e-12);
598 let f_rev = max_flow_value(&g, 1, 0, None).unwrap();
599 assert_close(f_rev, 1.0, 1e-12);
600 }
601
602 #[test]
603 fn two_parallel_paths_directed() {
604 let mut g = Graph::new(4, true).unwrap();
606 g.add_edge(0, 1).unwrap();
607 g.add_edge(1, 3).unwrap();
608 g.add_edge(0, 2).unwrap();
609 g.add_edge(2, 3).unwrap();
610 let f = max_flow_value(&g, 0, 3, None).unwrap();
611 assert_close(f, 2.0, 1e-12);
612 }
613
614 #[test]
615 fn bottleneck_directed() {
616 let mut g = Graph::new(4, true).unwrap();
618 g.add_edge(0, 1).unwrap();
619 g.add_edge(1, 2).unwrap();
620 g.add_edge(2, 3).unwrap();
621 let cap = vec![5.0, 2.0, 5.0];
622 let f = max_flow_value(&g, 0, 3, Some(&cap)).unwrap();
623 assert_close(f, 2.0, 1e-12);
624 }
625
626 #[test]
627 fn classic_max_flow_textbook() {
628 let mut g = Graph::new(6, true).unwrap();
633 g.add_edge(0, 1).unwrap();
634 g.add_edge(0, 2).unwrap();
635 g.add_edge(1, 3).unwrap();
636 g.add_edge(2, 1).unwrap();
637 g.add_edge(2, 4).unwrap();
638 g.add_edge(3, 2).unwrap();
639 g.add_edge(3, 5).unwrap();
640 g.add_edge(4, 3).unwrap();
641 g.add_edge(4, 5).unwrap();
642 let cap = vec![16.0, 13.0, 12.0, 4.0, 14.0, 9.0, 20.0, 7.0, 4.0];
643 let f = max_flow_value(&g, 0, 5, Some(&cap)).unwrap();
644 assert_close(f, 23.0, 1e-12);
645 }
646
647 #[test]
648 fn multigraph_parallel_edges() {
649 let mut g = Graph::new(2, true).unwrap();
651 g.add_edge(0, 1).unwrap();
652 g.add_edge(0, 1).unwrap();
653 g.add_edge(0, 1).unwrap();
654 let cap = vec![1.0, 2.0, 4.0];
655 let f = max_flow_value(&g, 0, 1, Some(&cap)).unwrap();
656 assert_close(f, 7.0, 1e-12);
657 }
658
659 #[test]
660 fn self_loop_does_not_contribute() {
661 let mut g = Graph::new(2, true).unwrap();
663 g.add_edge(0, 0).unwrap();
664 g.add_edge(0, 1).unwrap();
665 let cap = vec![100.0, 3.0];
666 let f = max_flow_value(&g, 0, 1, Some(&cap)).unwrap();
667 assert_close(f, 3.0, 1e-12);
668 }
669
670 #[test]
671 fn undirected_two_paths_share_capacity() {
672 let mut g = Graph::with_vertices(3);
678 g.add_edge(0, 1).unwrap();
679 g.add_edge(1, 2).unwrap();
680 let f = max_flow_value(&g, 0, 2, None).unwrap();
681 assert_close(f, 1.0, 1e-12);
682 }
683
684 #[test]
685 fn weighted_fractional_flow() {
686 let mut g = Graph::new(4, true).unwrap();
688 g.add_edge(0, 1).unwrap();
689 g.add_edge(1, 3).unwrap();
690 g.add_edge(0, 2).unwrap();
691 g.add_edge(2, 3).unwrap();
692 let cap = vec![0.5, 0.5, 0.25, 0.25];
693 let f = max_flow_value(&g, 0, 3, Some(&cap)).unwrap();
694 assert_close(f, 0.75, 1e-12);
695 }
696
697 fn validate_flow(graph: &Graph, result: &MaxFlow, capacity: Option<&[f64]>) {
700 let m = graph.ecount();
701 let n = graph.vcount();
702 let directed = graph.is_directed();
703
704 assert_eq!(result.flow.len(), m, "flow.len() must equal ecount()");
706
707 for e in 0..m {
709 let cap_e = capacity.map_or(1.0, |c| c[e]);
710 assert!(
711 result.flow[e].abs() <= cap_e + 1e-12,
712 "flow[{e}] = {} exceeds capacity {cap_e}",
713 result.flow[e]
714 );
715 if directed {
716 assert!(
717 result.flow[e] >= -1e-12,
718 "directed flow[{e}] = {} must be non-negative",
719 result.flow[e]
720 );
721 }
722 }
723
724 if directed {
728 for v in 0..n {
729 if v == result.partition[0]
730 || !result.partition2.is_empty()
731 && v == *result.partition2.last().unwrap_or(&u32::MAX)
732 {
733 continue;
734 }
735 let mut net = 0.0_f64;
736 for e in 0..m {
737 let (src, dst) = graph.edge(e as u32).unwrap();
738 if dst == v {
739 net += result.flow[e];
740 }
741 if src == v {
742 net -= result.flow[e];
743 }
744 }
745 assert!(
746 net.abs() < 1e-9,
747 "flow conservation violated at vertex {v}: net = {net}"
748 );
749 }
750 }
751
752 assert_eq!(result.partition.len() + result.partition2.len(), n as usize);
754 assert!(result.partition.windows(2).all(|w| w[0] < w[1]));
755 assert!(result.partition2.windows(2).all(|w| w[0] < w[1]));
756
757 let cut_sum: f64 = result
759 .cut
760 .iter()
761 .map(|&e| capacity.map_or(1.0, |c| c[e as usize]))
762 .sum();
763 assert_close(cut_sum, result.value, 1e-9 * result.value.abs().max(1.0));
764
765 let mut in_source = vec![false; n as usize];
767 for &v in &result.partition {
768 in_source[v as usize] = true;
769 }
770 for &eid in &result.cut {
771 let (u, v) = graph.edge(eid).unwrap();
772 if directed {
773 assert!(
774 in_source[u as usize] && !in_source[v as usize],
775 "directed cut edge {eid} must go S→V\\S"
776 );
777 } else {
778 assert_ne!(
779 in_source[u as usize], in_source[v as usize],
780 "cut edge {eid} must cross partition"
781 );
782 }
783 }
784 }
785
786 #[test]
787 fn max_flow_two_parallel_paths() {
788 let mut g = Graph::new(4, true).unwrap();
789 g.add_edge(0, 1).unwrap();
790 g.add_edge(1, 3).unwrap();
791 g.add_edge(0, 2).unwrap();
792 g.add_edge(2, 3).unwrap();
793 let cap = vec![1.0, 1.0, 1.0, 1.0];
794 let r = max_flow(&g, 0, 3, Some(&cap)).unwrap();
795 assert_close(r.value, 2.0, 1e-12);
796 validate_flow(&g, &r, Some(&cap));
797 }
798
799 #[test]
800 fn max_flow_bottleneck() {
801 let mut g = Graph::new(4, true).unwrap();
802 g.add_edge(0, 1).unwrap();
803 g.add_edge(1, 2).unwrap();
804 g.add_edge(2, 3).unwrap();
805 let cap = vec![5.0, 2.0, 7.0];
806 let r = max_flow(&g, 0, 3, Some(&cap)).unwrap();
807 assert_close(r.value, 2.0, 1e-12);
808 assert_close(r.flow[0], 2.0, 1e-12);
809 assert_close(r.flow[1], 2.0, 1e-12);
810 assert_close(r.flow[2], 2.0, 1e-12);
811 validate_flow(&g, &r, Some(&cap));
812 }
813
814 #[test]
815 fn max_flow_classic_textbook() {
816 let mut g = Graph::new(6, true).unwrap();
817 g.add_edge(0, 1).unwrap();
818 g.add_edge(0, 2).unwrap();
819 g.add_edge(1, 3).unwrap();
820 g.add_edge(2, 1).unwrap();
821 g.add_edge(2, 4).unwrap();
822 g.add_edge(3, 2).unwrap();
823 g.add_edge(3, 5).unwrap();
824 g.add_edge(4, 3).unwrap();
825 g.add_edge(4, 5).unwrap();
826 let cap = vec![16.0, 13.0, 12.0, 4.0, 14.0, 9.0, 20.0, 7.0, 4.0];
827 let r = max_flow(&g, 0, 5, Some(&cap)).unwrap();
828 assert_close(r.value, 23.0, 1e-12);
829 validate_flow(&g, &r, Some(&cap));
830 }
831
832 #[test]
833 fn max_flow_isolated_endpoints() {
834 let g = Graph::with_vertices(4);
835 let r = max_flow(&g, 0, 3, None).unwrap();
836 assert_close(r.value, 0.0, 1e-12);
837 assert!(r.flow.iter().all(|&f| f.abs() < 1e-12));
838 assert!(r.cut.is_empty());
839 }
840
841 #[test]
842 fn max_flow_single_edge() {
843 let mut g = Graph::new(2, true).unwrap();
844 g.add_edge(0, 1).unwrap();
845 let r = max_flow(&g, 0, 1, None).unwrap();
846 assert_close(r.value, 1.0, 1e-12);
847 assert_close(r.flow[0], 1.0, 1e-12);
848 validate_flow(&g, &r, None);
849 }
850
851 #[test]
852 fn max_flow_undirected() {
853 let mut g = Graph::with_vertices(4);
854 g.add_edge(0, 1).unwrap();
855 g.add_edge(0, 2).unwrap();
856 g.add_edge(1, 3).unwrap();
857 g.add_edge(2, 3).unwrap();
858 let r = max_flow(&g, 0, 3, None).unwrap();
859 assert_close(r.value, 2.0, 1e-12);
860 assert_eq!(r.flow.len(), 4);
861 validate_flow(&g, &r, None);
862 }
863
864 #[test]
865 fn max_flow_value_matches_full() {
866 let mut g = Graph::new(5, true).unwrap();
867 for (s, t) in [(0u32, 1u32), (0, 2), (1, 3), (2, 3), (3, 4), (1, 4)] {
868 g.add_edge(s, t).unwrap();
869 }
870 let caps = [3.0, 5.0, 2.0, 4.0, 6.0, 1.0];
871 let scalar = max_flow_value(&g, 0, 4, Some(&caps)).unwrap();
872 let full = max_flow(&g, 0, 4, Some(&caps)).unwrap();
873 assert_close(scalar, full.value, 1e-12);
874 validate_flow(&g, &full, Some(&caps));
875 }
876}
877
878#[cfg(all(test, feature = "proptest-harness"))]
879mod proptest_tests {
880 use super::*;
881 use proptest::prelude::*;
882
883 fn edmonds_karp(graph: &Graph, source: u32, target: u32, cap: &[f64]) -> f64 {
889 let net = Network::build(graph, Some(cap)).expect("net builds");
890 let n = net.n;
891 let mut residual = net.cap.clone();
892 let mut total = 0.0_f64;
893 loop {
894 let mut parent_arc = vec![u32::MAX; n];
895 let mut visited = vec![false; n];
896 visited[source as usize] = true;
897 let mut queue = vec![source];
898 let mut head = 0_usize;
899 let mut found = false;
900 'bfs: while head < queue.len() {
901 let v = queue[head] as usize;
902 head += 1;
903 for &a in &net.arcs_out[v] {
904 let a_us = a as usize;
905 let w = net.head[a_us] as usize;
906 if !visited[w] && residual[a_us] > 0.0 {
907 visited[w] = true;
908 parent_arc[w] = a;
909 if w == target as usize {
910 found = true;
911 break 'bfs;
912 }
913 queue.push(w as u32);
914 }
915 }
916 }
917 if !found {
918 break;
919 }
920 let mut bottleneck = f64::INFINITY;
921 let mut cur = target as usize;
922 while cur != source as usize {
923 let a = parent_arc[cur] as usize;
924 if residual[a] < bottleneck {
925 bottleneck = residual[a];
926 }
927 cur = net.head[a ^ 1] as usize;
928 }
929 let mut cur = target as usize;
930 while cur != source as usize {
931 let a = parent_arc[cur] as usize;
932 residual[a] -= bottleneck;
933 residual[a ^ 1] += bottleneck;
934 cur = net.head[a ^ 1] as usize;
935 }
936 total += bottleneck;
937 }
938 total
939 }
940
941 proptest! {
942 #![proptest_config(ProptestConfig::with_cases(80))]
943
944 #[test]
945 fn matches_edmonds_karp_directed(
946 n in 2u32..8,
947 seed in any::<u64>(),
948 ) {
949 let mut rng_state = seed | 1;
951 let mut next = || {
952 rng_state ^= rng_state << 13;
953 rng_state ^= rng_state >> 7;
954 rng_state ^= rng_state << 17;
955 rng_state
956 };
957 let mut g = Graph::new(n, true).unwrap();
958 let edge_count = next() as u32 % (n * 3 + 1);
959 let mut caps = Vec::with_capacity(edge_count as usize);
960 for _ in 0..edge_count {
961 let u = (next() as u32) % n;
962 let v = (next() as u32) % n;
963 g.add_edge(u, v).unwrap();
964 let c = f64::from((next() as u32 % 16) + 1);
965 caps.push(c);
966 }
967 let source = 0;
968 let target = n - 1;
969 if source == target {
970 return Ok(());
971 }
972 let dinic = max_flow_value(&g, source, target, Some(&caps)).unwrap();
973 let ref_val = edmonds_karp(&g, source, target, &caps);
974 prop_assert!(
975 (dinic - ref_val).abs() < 1e-9,
976 "dinic={dinic} ref={ref_val}"
977 );
978 }
979
980 #[test]
981 fn matches_edmonds_karp_undirected(
982 n in 2u32..8,
983 seed in any::<u64>(),
984 ) {
985 let mut rng_state = seed | 1;
986 let mut next = || {
987 rng_state ^= rng_state << 13;
988 rng_state ^= rng_state >> 7;
989 rng_state ^= rng_state << 17;
990 rng_state
991 };
992 let mut g = Graph::with_vertices(n);
993 let edge_count = next() as u32 % (n * 3 + 1);
994 let mut caps = Vec::with_capacity(edge_count as usize);
995 for _ in 0..edge_count {
996 let u = (next() as u32) % n;
997 let v = (next() as u32) % n;
998 g.add_edge(u, v).unwrap();
999 let c = f64::from((next() as u32 % 16) + 1);
1000 caps.push(c);
1001 }
1002 let source = 0;
1003 let target = n - 1;
1004 if source == target {
1005 return Ok(());
1006 }
1007 let dinic = max_flow_value(&g, source, target, Some(&caps)).unwrap();
1008 let ref_val = edmonds_karp(&g, source, target, &caps);
1009 prop_assert!(
1010 (dinic - ref_val).abs() < 1e-9,
1011 "dinic={dinic} ref={ref_val}"
1012 );
1013 }
1014 }
1015}