1use std::cmp::Reverse;
4use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
5
6use super::types::{
7 AllPairsShortestPath, FlowNetwork, MatchingResult, SpanningTree, StronglyConnectedComponents,
8 WGraph,
9};
10
11struct Dsu {
14 parent: Vec<usize>,
15 rank: Vec<usize>,
16}
17
18impl Dsu {
19 fn new(n: usize) -> Self {
20 Self {
21 parent: (0..n).collect(),
22 rank: vec![0; n],
23 }
24 }
25
26 fn find(&mut self, x: usize) -> usize {
27 if self.parent[x] != x {
28 self.parent[x] = self.find(self.parent[x]);
29 }
30 self.parent[x]
31 }
32
33 fn union(&mut self, a: usize, b: usize) -> bool {
34 let ra = self.find(a);
35 let rb = self.find(b);
36 if ra == rb {
37 return false;
38 }
39 match self.rank[ra].cmp(&self.rank[rb]) {
40 std::cmp::Ordering::Less => self.parent[ra] = rb,
41 std::cmp::Ordering::Greater => self.parent[rb] = ra,
42 std::cmp::Ordering::Equal => {
43 self.parent[rb] = ra;
44 self.rank[ra] += 1;
45 }
46 }
47 true
48 }
49}
50
51pub fn dijkstra(g: &WGraph, start: usize) -> Vec<Option<i64>> {
58 let n = g.n;
59 let inf = i64::MAX / 2;
60 let mut dist = vec![inf; n];
61 dist[start] = 0;
62 let mut heap: BinaryHeap<Reverse<(i64, usize)>> = BinaryHeap::new();
63 heap.push(Reverse((0, start)));
64
65 while let Some(Reverse((d, u))) = heap.pop() {
66 if d > dist[u] {
67 continue;
68 }
69 for &(v, w) in &g.adj[u] {
70 let nd = dist[u].saturating_add(w);
71 if nd < dist[v] {
72 dist[v] = nd;
73 heap.push(Reverse((nd, v)));
74 }
75 }
76 }
77
78 dist.into_iter()
79 .map(|d| if d == inf { None } else { Some(d) })
80 .collect()
81}
82
83pub fn bellman_ford(g: &WGraph, start: usize) -> Result<Vec<Option<i64>>, String> {
88 let n = g.n;
89 let inf = i64::MAX / 2;
90 let mut dist = vec![inf; n];
91 dist[start] = 0;
92
93 for _ in 0..n.saturating_sub(1) {
95 let mut changed = false;
96 for u in 0..n {
97 if dist[u] == inf {
98 continue;
99 }
100 for &(v, w) in &g.adj[u] {
101 let nd = dist[u].saturating_add(w);
102 if nd < dist[v] {
103 dist[v] = nd;
104 changed = true;
105 }
106 }
107 }
108 if !changed {
109 break;
110 }
111 }
112
113 for u in 0..n {
115 if dist[u] == inf {
116 continue;
117 }
118 for &(v, w) in &g.adj[u] {
119 let nd = dist[u].saturating_add(w);
120 if nd < dist[v] {
121 return Err(format!(
122 "Negative-weight cycle detected reachable from vertex {}",
123 start
124 ));
125 }
126 }
127 }
128
129 Ok(dist
130 .into_iter()
131 .map(|d| if d == inf { None } else { Some(d) })
132 .collect())
133}
134
135pub fn floyd_warshall(g: &WGraph) -> AllPairsShortestPath {
140 let n = g.n;
141 let inf = i64::MAX / 4;
142
143 let mut d = vec![vec![inf; n]; n];
144 let mut next: Vec<Vec<Option<usize>>> = vec![vec![None; n]; n];
145
146 for u in 0..n {
147 d[u][u] = 0;
148 for &(v, w) in &g.adj[u] {
149 if w < d[u][v] {
150 d[u][v] = w;
151 next[u][v] = Some(v);
152 }
153 }
154 }
155
156 for k in 0..n {
157 for i in 0..n {
158 for j in 0..n {
159 if d[i][k] < inf && d[k][j] < inf {
160 let through = d[i][k] + d[k][j];
161 if through < d[i][j] {
162 d[i][j] = through;
163 next[i][j] = next[i][k];
164 }
165 }
166 }
167 }
168 }
169
170 let dist = d
172 .into_iter()
173 .map(|row| {
174 row.into_iter()
175 .map(|v| if v >= inf { None } else { Some(v) })
176 .collect()
177 })
178 .collect();
179
180 AllPairsShortestPath { dist, next }
181}
182
183pub fn kruskal_mst(n: usize, edges: &[(usize, usize, i64)]) -> SpanningTree {
190 let mut sorted: Vec<(i64, usize, usize)> = edges.iter().map(|&(u, v, w)| (w, u, v)).collect();
191 sorted.sort_unstable();
192
193 let mut dsu = Dsu::new(n);
194 let mut tree_edges: Vec<(usize, usize, i64)> = Vec::new();
195 let mut total_weight = 0i64;
196
197 for (w, u, v) in sorted {
198 if dsu.union(u, v) {
199 tree_edges.push((u, v, w));
200 total_weight += w;
201 }
202 }
203
204 SpanningTree {
205 edges: tree_edges,
206 total_weight,
207 }
208}
209
210pub fn prim_mst(g: &WGraph) -> SpanningTree {
215 let n = g.n;
216 if n == 0 {
217 return SpanningTree {
218 edges: vec![],
219 total_weight: 0,
220 };
221 }
222
223 let mut adj: Vec<Vec<(usize, i64)>> = vec![vec![]; n];
225 for u in 0..n {
226 for &(v, w) in &g.adj[u] {
227 adj[u].push((v, w));
228 adj[v].push((u, w));
229 }
230 }
231
232 let inf = i64::MAX / 2;
233 let mut in_tree = vec![false; n];
234 let mut min_edge: Vec<i64> = vec![inf; n];
235 let mut parent: Vec<Option<usize>> = vec![None; n];
236 min_edge[0] = 0;
237
238 let mut heap: BinaryHeap<Reverse<(i64, usize, usize)>> = BinaryHeap::new();
240 heap.push(Reverse((0, 0, 0)));
241
242 let mut tree_edges: Vec<(usize, usize, i64)> = Vec::new();
243 let mut total_weight = 0i64;
244
245 while let Some(Reverse((cost, u, from))) = heap.pop() {
246 if in_tree[u] {
247 continue;
248 }
249 in_tree[u] = true;
250 if u != 0 {
251 tree_edges.push((from, u, cost));
252 total_weight += cost;
253 }
254
255 for &(v, w) in &adj[u] {
256 if !in_tree[v] && w < min_edge[v] {
257 min_edge[v] = w;
258 parent[v] = Some(u);
259 heap.push(Reverse((w, v, u)));
260 }
261 }
262 }
263
264 SpanningTree {
265 edges: tree_edges,
266 total_weight,
267 }
268}
269
270pub fn max_flow_bfs(net: &mut FlowNetwork) -> i64 {
276 let source = net.source;
277 let sink = net.sink;
278 let mut total_flow = 0i64;
279
280 loop {
281 let mut prev_edge: Vec<Option<usize>> = vec![None; net.n];
283 let mut visited = vec![false; net.n];
284 let mut queue = VecDeque::new();
285 visited[source] = true;
286 queue.push_back(source);
287
288 'bfs: while let Some(u) = queue.pop_front() {
289 for &eid in &net.graph[u].clone() {
290 let v = net.edges[eid].to;
291 if !visited[v] && net.residual(eid) > 0 {
292 visited[v] = true;
293 prev_edge[v] = Some(eid);
294 if v == sink {
295 break 'bfs;
296 }
297 queue.push_back(v);
298 }
299 }
300 }
301
302 if !visited[sink] {
303 break;
304 }
305
306 let mut bottleneck = i64::MAX;
308 let mut cur = sink;
309 while cur != source {
310 if let Some(eid) = prev_edge[cur] {
311 bottleneck = bottleneck.min(net.residual(eid));
312 cur = net.edges[eid].from;
313 } else {
314 break;
315 }
316 }
317
318 cur = sink;
320 while cur != source {
321 if let Some(eid) = prev_edge[cur] {
322 net.edges[eid].flow += bottleneck;
323 let rev = net.edges[eid].rev;
324 net.edges[rev].flow -= bottleneck;
325 cur = net.edges[eid].from;
326 } else {
327 break;
328 }
329 }
330
331 total_flow += bottleneck;
332 }
333
334 total_flow
335}
336
337pub fn min_cut(net: &FlowNetwork, _max_flow_val: i64) -> (Vec<usize>, Vec<usize>) {
342 let source = net.source;
343 let n = net.n;
344 let mut reachable = vec![false; n];
345 let mut queue = VecDeque::new();
346 reachable[source] = true;
347 queue.push_back(source);
348
349 while let Some(u) = queue.pop_front() {
350 for &eid in &net.graph[u] {
351 let v = net.edges[eid].to;
352 if !reachable[v] && net.residual(eid) > 0 {
353 reachable[v] = true;
354 queue.push_back(v);
355 }
356 }
357 }
358
359 let s_side: Vec<usize> = (0..n).filter(|&v| reachable[v]).collect();
360 let t_side: Vec<usize> = (0..n).filter(|&v| !reachable[v]).collect();
361 (s_side, t_side)
362}
363
364pub fn bipartite_matching(left: usize, right: usize, edges: &[(usize, usize)]) -> MatchingResult {
372 let mut adj: Vec<Vec<usize>> = vec![vec![]; left];
374 for &(l, r) in edges {
375 adj[l].push(r);
376 }
377
378 let inf = usize::MAX;
379 let mut match_l: Vec<Option<usize>> = vec![None; left];
380 let mut match_r: Vec<Option<usize>> = vec![None; right];
381
382 loop {
383 let mut dist: Vec<usize> = vec![inf; left];
385 let mut queue = VecDeque::new();
386
387 for l in 0..left {
388 if match_l[l].is_none() {
389 dist[l] = 0;
390 queue.push_back(l);
391 }
392 }
393
394 let mut found = false;
395 while let Some(l) = queue.pop_front() {
396 for &r in &adj[l] {
397 let nl = match_r[r];
398 match nl {
399 None => {
400 found = true;
401 }
402 Some(nl_idx) => {
403 if dist[nl_idx] == inf {
404 dist[nl_idx] = dist[l] + 1;
405 queue.push_back(nl_idx);
406 }
407 }
408 }
409 }
410 }
411
412 if !found {
413 break;
414 }
415
416 for l in 0..left {
418 if match_l[l].is_none() {
419 dfs_hopcroft(l, &adj, &mut match_l, &mut match_r, &mut dist, inf);
420 }
421 }
422 }
423
424 let size = match_l.iter().filter(|m| m.is_some()).count();
425 MatchingResult {
426 matching: match_l,
427 size,
428 }
429}
430
431fn dfs_hopcroft(
432 l: usize,
433 adj: &[Vec<usize>],
434 match_l: &mut Vec<Option<usize>>,
435 match_r: &mut Vec<Option<usize>>,
436 dist: &mut Vec<usize>,
437 inf: usize,
438) -> bool {
439 for &r in &adj[l] {
440 let ok = match match_r[r] {
441 None => true,
442 Some(nl) => {
443 dist[nl] == dist[l] + 1 && dfs_hopcroft(nl, adj, match_l, match_r, dist, inf)
444 }
445 };
446 if ok {
447 match_l[l] = Some(r);
448 match_r[r] = Some(l);
449 return true;
450 }
451 }
452 dist[l] = inf;
453 false
454}
455
456pub fn tarjan_scc(g: &WGraph) -> StronglyConnectedComponents {
462 let n = g.n;
463 let mut index_counter = 0usize;
464 let mut stack: Vec<usize> = Vec::new();
465 let mut on_stack = vec![false; n];
466 let mut index: Vec<Option<usize>> = vec![None; n];
467 let mut lowlink = vec![0usize; n];
468 let mut components: Vec<Vec<usize>> = Vec::new();
469 let mut component_of: Vec<usize> = vec![0; n];
470
471 for v in 0..n {
472 if index[v].is_none() {
473 tarjan_visit(
474 v,
475 g,
476 &mut index_counter,
477 &mut stack,
478 &mut on_stack,
479 &mut index,
480 &mut lowlink,
481 &mut components,
482 &mut component_of,
483 );
484 }
485 }
486
487 StronglyConnectedComponents {
488 components,
489 component_of,
490 }
491}
492
493#[allow(clippy::too_many_arguments)]
494fn tarjan_visit(
495 v: usize,
496 g: &WGraph,
497 index_counter: &mut usize,
498 stack: &mut Vec<usize>,
499 on_stack: &mut Vec<bool>,
500 index: &mut Vec<Option<usize>>,
501 lowlink: &mut Vec<usize>,
502 components: &mut Vec<Vec<usize>>,
503 component_of: &mut Vec<usize>,
504) {
505 let mut call_stack: Vec<(usize, usize)> = vec![(v, 0)];
508 index[v] = Some(*index_counter);
509 lowlink[v] = *index_counter;
510 *index_counter += 1;
511 stack.push(v);
512 on_stack[v] = true;
513
514 while let Some((u, ei)) = call_stack.last_mut() {
515 let u = *u;
516 let neighbors = &g.adj[u];
517 if *ei < neighbors.len() {
518 let (w, _) = neighbors[*ei];
519 *ei += 1;
520 if index[w].is_none() {
521 index[w] = Some(*index_counter);
523 lowlink[w] = *index_counter;
524 *index_counter += 1;
525 stack.push(w);
526 on_stack[w] = true;
527 call_stack.push((w, 0));
528 } else if on_stack[w] {
529 let lw = lowlink[w];
530 let lu = lowlink[u];
531 lowlink[u] = lu.min(lw);
532 if let Some(frame) = call_stack.last_mut() {
534 let _ = frame; }
536 }
537 } else {
538 call_stack.pop();
540
541 if let Some(&(parent, _)) = call_stack.last() {
543 lowlink[parent] = lowlink[parent].min(lowlink[u]);
544 }
545
546 if let Some(idx_u) = index[u] {
548 if lowlink[u] == idx_u {
549 let comp_id = components.len();
550 let mut comp = Vec::new();
551 loop {
552 let w = stack.pop().unwrap_or(u);
553 on_stack[w] = false;
554 comp.push(w);
555 if w == u {
556 break;
557 }
558 }
559 for &node in &comp {
560 component_of[node] = comp_id;
561 }
562 components.push(comp);
563 }
564 }
565 }
566 }
567}
568
569pub fn topological_sort_dag(g: &WGraph) -> Option<Vec<usize>> {
575 let n = g.n;
576 let mut in_degree = vec![0usize; n];
577 for u in 0..n {
578 for &(v, _) in &g.adj[u] {
579 in_degree[v] += 1;
580 }
581 }
582
583 let mut queue: VecDeque<usize> = (0..n).filter(|&u| in_degree[u] == 0).collect();
584 let mut order = Vec::with_capacity(n);
585
586 while let Some(u) = queue.pop_front() {
587 order.push(u);
588 for &(v, _) in &g.adj[u] {
589 in_degree[v] -= 1;
590 if in_degree[v] == 0 {
591 queue.push_back(v);
592 }
593 }
594 }
595
596 if order.len() == n {
597 Some(order)
598 } else {
599 None
600 }
601}
602
603pub fn is_bipartite(g: &WGraph) -> Option<(Vec<usize>, Vec<usize>)> {
609 let n = g.n;
610 let no_color = usize::MAX;
611 let mut color = vec![no_color; n];
612
613 for start in 0..n {
614 if color[start] != no_color {
615 continue;
616 }
617 color[start] = 0;
618 let mut queue = VecDeque::new();
619 queue.push_back(start);
620
621 while let Some(u) = queue.pop_front() {
622 for &(v, _) in &g.adj[u] {
623 if color[v] == no_color {
624 color[v] = 1 - color[u];
625 queue.push_back(v);
626 } else if color[v] == color[u] {
627 return None;
628 }
629 }
630 }
631 }
632
633 let left: Vec<usize> = (0..n).filter(|&v| color[v] == 0).collect();
634 let right: Vec<usize> = (0..n).filter(|&v| color[v] == 1).collect();
635 Some((left, right))
636}
637
638pub fn chromatic_number_approx(g: &WGraph) -> usize {
644 let n = g.n;
645 if n == 0 {
646 return 0;
647 }
648
649 let mut undirected: Vec<HashSet<usize>> = vec![HashSet::new(); n];
651 for u in 0..n {
652 for &(v, _) in &g.adj[u] {
653 undirected[u].insert(v);
654 undirected[v].insert(u);
655 }
656 }
657
658 let mut color = vec![usize::MAX; n];
659 let mut max_color = 0usize;
660
661 for u in 0..n {
662 let neighbor_colors: HashSet<usize> = undirected[u]
663 .iter()
664 .filter_map(|&v| {
665 if color[v] != usize::MAX {
666 Some(color[v])
667 } else {
668 None
669 }
670 })
671 .collect();
672
673 let mut c = 0usize;
674 while neighbor_colors.contains(&c) {
675 c += 1;
676 }
677 color[u] = c;
678 if c > max_color {
679 max_color = c;
680 }
681 }
682
683 max_color + 1
684}
685
686#[cfg(test)]
689mod tests {
690 use super::super::types::{FlowNetwork, WGraph};
691 use super::*;
692
693 fn simple_dag() -> WGraph {
694 let mut g = WGraph::new(4);
696 g.add_edge(0, 1, 1);
697 g.add_edge(0, 2, 4);
698 g.add_edge(1, 2, 2);
699 g.add_edge(1, 3, 5);
700 g.add_edge(2, 3, 1);
701 g
702 }
703
704 #[test]
707 fn test_dijkstra_basic() {
708 let g = simple_dag();
709 let dist = dijkstra(&g, 0);
710 assert_eq!(dist[0], Some(0));
711 assert_eq!(dist[1], Some(1));
712 assert_eq!(dist[2], Some(3)); assert_eq!(dist[3], Some(4)); }
715
716 #[test]
717 fn test_dijkstra_unreachable() {
718 let mut g = WGraph::new(3);
719 g.add_edge(0, 1, 5);
720 let dist = dijkstra(&g, 0);
722 assert_eq!(dist[2], None);
723 }
724
725 #[test]
726 fn test_dijkstra_single_vertex() {
727 let g = WGraph::new(1);
728 let dist = dijkstra(&g, 0);
729 assert_eq!(dist[0], Some(0));
730 }
731
732 #[test]
733 fn test_dijkstra_self_loop() {
734 let mut g = WGraph::new(2);
735 g.add_edge(0, 0, 5);
736 g.add_edge(0, 1, 3);
737 let dist = dijkstra(&g, 0);
738 assert_eq!(dist[1], Some(3));
739 }
740
741 #[test]
744 fn test_bellman_ford_positive_weights() {
745 let g = simple_dag();
746 let dist = bellman_ford(&g, 0).expect("no negative cycle");
747 assert_eq!(dist[3], Some(4));
748 }
749
750 #[test]
751 fn test_bellman_ford_negative_weights() {
752 let mut g = WGraph::new(3);
753 g.add_edge(0, 1, -2);
754 g.add_edge(1, 2, 3);
755 g.add_edge(0, 2, 4);
756 let dist = bellman_ford(&g, 0).expect("no negative cycle");
757 assert_eq!(dist[2], Some(1)); }
759
760 #[test]
761 fn test_bellman_ford_negative_cycle() {
762 let mut g = WGraph::new(3);
763 g.add_edge(0, 1, 1);
764 g.add_edge(1, 2, -3);
765 g.add_edge(2, 0, 1);
766 let result = bellman_ford(&g, 0);
767 assert!(result.is_err());
768 }
769
770 #[test]
771 fn test_bellman_ford_unreachable() {
772 let mut g = WGraph::new(3);
773 g.add_edge(0, 1, 1);
774 let dist = bellman_ford(&g, 0).expect("no negative cycle");
775 assert_eq!(dist[2], None);
776 }
777
778 #[test]
781 fn test_floyd_warshall_basic() {
782 let g = simple_dag();
783 let apsp = floyd_warshall(&g);
784 assert_eq!(apsp.dist[0][3], Some(4));
785 assert_eq!(apsp.dist[1][3], Some(3));
786 }
787
788 #[test]
789 fn test_floyd_warshall_no_path() {
790 let mut g = WGraph::new(3);
791 g.add_edge(0, 1, 2);
792 let apsp = floyd_warshall(&g);
793 assert_eq!(apsp.dist[0][2], None);
794 assert_eq!(apsp.dist[1][2], None);
795 }
796
797 #[test]
798 fn test_floyd_warshall_next_hops() {
799 let mut g = WGraph::new(3);
800 g.add_edge(0, 1, 1);
801 g.add_edge(1, 2, 1);
802 let apsp = floyd_warshall(&g);
803 assert_eq!(apsp.next[0][2], Some(1)); }
805
806 #[test]
809 fn test_kruskal_basic() {
810 let edges = vec![(0, 1, 1), (0, 2, 3), (1, 2, 2), (1, 3, 4), (2, 3, 5)];
811 let mst = kruskal_mst(4, &edges);
812 assert_eq!(mst.edges.len(), 3);
813 assert_eq!(mst.total_weight, 7); }
815
816 #[test]
817 fn test_kruskal_empty() {
818 let mst = kruskal_mst(3, &[]);
819 assert_eq!(mst.edges.len(), 0);
820 assert_eq!(mst.total_weight, 0);
821 }
822
823 #[test]
824 fn test_kruskal_single_edge() {
825 let edges = vec![(0, 1, 5)];
826 let mst = kruskal_mst(2, &edges);
827 assert_eq!(mst.total_weight, 5);
828 }
829
830 #[test]
833 fn test_prim_basic() {
834 let mut g = WGraph::new(4);
835 g.add_edge(0, 1, 1);
836 g.add_edge(1, 0, 1);
837 g.add_edge(0, 2, 3);
838 g.add_edge(2, 0, 3);
839 g.add_edge(1, 2, 2);
840 g.add_edge(2, 1, 2);
841 g.add_edge(1, 3, 4);
842 g.add_edge(3, 1, 4);
843 let mst = prim_mst(&g);
844 assert_eq!(mst.edges.len(), 3);
845 assert_eq!(mst.total_weight, 7);
846 }
847
848 #[test]
849 fn test_prim_empty() {
850 let g = WGraph::new(0);
851 let mst = prim_mst(&g);
852 assert_eq!(mst.edges.len(), 0);
853 }
854
855 #[test]
858 fn test_max_flow_basic() {
859 let mut net = FlowNetwork::new(4, 0, 3);
860 net.add_edge(0, 1, 3);
861 net.add_edge(0, 2, 2);
862 net.add_edge(1, 3, 2);
863 net.add_edge(2, 3, 3);
864 let flow = max_flow_bfs(&mut net);
865 assert_eq!(flow, 4);
866 }
867
868 #[test]
869 fn test_max_flow_no_path() {
870 let mut net = FlowNetwork::new(4, 0, 3);
871 net.add_edge(0, 1, 10);
872 net.add_edge(2, 3, 10);
873 let flow = max_flow_bfs(&mut net);
875 assert_eq!(flow, 0);
876 }
877
878 #[test]
879 fn test_max_flow_single_edge() {
880 let mut net = FlowNetwork::new(2, 0, 1);
881 net.add_edge(0, 1, 7);
882 let flow = max_flow_bfs(&mut net);
883 assert_eq!(flow, 7);
884 }
885
886 #[test]
889 fn test_min_cut_basic() {
890 let mut net = FlowNetwork::new(4, 0, 3);
891 net.add_edge(0, 1, 3);
892 net.add_edge(0, 2, 2);
893 net.add_edge(1, 3, 2);
894 net.add_edge(2, 3, 3);
895 let flow_val = max_flow_bfs(&mut net);
896 let (s_side, t_side) = min_cut(&net, flow_val);
897 assert!(s_side.contains(&0));
898 assert!(t_side.contains(&3));
899 }
900
901 #[test]
904 fn test_bipartite_matching_perfect() {
905 let edges: Vec<(usize, usize)> = (0..3).flat_map(|l| (0..3).map(move |r| (l, r))).collect();
907 let result = bipartite_matching(3, 3, &edges);
908 assert_eq!(result.size, 3);
909 }
910
911 #[test]
912 fn test_bipartite_matching_partial() {
913 let edges = vec![(0, 0)];
915 let result = bipartite_matching(2, 2, &edges);
916 assert_eq!(result.size, 1);
917 assert_eq!(result.matching[0], Some(0));
918 }
919
920 #[test]
921 fn test_bipartite_matching_empty() {
922 let result = bipartite_matching(3, 3, &[]);
923 assert_eq!(result.size, 0);
924 }
925
926 #[test]
927 fn test_bipartite_matching_chain() {
928 let edges = vec![(0, 0), (1, 1), (2, 2)];
930 let result = bipartite_matching(3, 3, &edges);
931 assert_eq!(result.size, 3);
932 }
933
934 #[test]
937 fn test_tarjan_scc_single_cycle() {
938 let mut g = WGraph::new(3);
939 g.add_edge(0, 1, 1);
940 g.add_edge(1, 2, 1);
941 g.add_edge(2, 0, 1);
942 let scc = tarjan_scc(&g);
943 assert_eq!(scc.components.len(), 1);
944 assert_eq!(scc.components[0].len(), 3);
945 }
946
947 #[test]
948 fn test_tarjan_scc_dag() {
949 let g = simple_dag();
950 let scc = tarjan_scc(&g);
951 assert_eq!(scc.components.len(), 4);
953 }
954
955 #[test]
956 fn test_tarjan_scc_two_components() {
957 let mut g = WGraph::new(4);
958 g.add_edge(0, 1, 1);
959 g.add_edge(1, 0, 1);
960 g.add_edge(2, 3, 1);
961 g.add_edge(3, 2, 1);
962 let scc = tarjan_scc(&g);
963 assert_eq!(scc.components.len(), 2);
964 }
965
966 #[test]
969 fn test_topological_sort_dag() {
970 let g = simple_dag();
971 let order = topological_sort_dag(&g).expect("DAG");
972 let pos: Vec<usize> = {
974 let mut p = vec![0usize; g.n];
975 for (i, &v) in order.iter().enumerate() {
976 p[v] = i;
977 }
978 p
979 };
980 for u in 0..g.n {
981 for &(v, _) in &g.adj[u] {
982 assert!(pos[u] < pos[v], "edge {} → {} violates order", u, v);
983 }
984 }
985 }
986
987 #[test]
988 fn test_topological_sort_cyclic() {
989 let mut g = WGraph::new(3);
990 g.add_edge(0, 1, 1);
991 g.add_edge(1, 2, 1);
992 g.add_edge(2, 0, 1);
993 assert!(topological_sort_dag(&g).is_none());
994 }
995
996 #[test]
999 fn test_is_bipartite_yes() {
1000 let mut g = WGraph::new(4);
1002 g.add_edge(0, 1, 1);
1003 g.add_edge(1, 0, 1);
1004 g.add_edge(1, 2, 1);
1005 g.add_edge(2, 1, 1);
1006 g.add_edge(2, 3, 1);
1007 g.add_edge(3, 2, 1);
1008 g.add_edge(3, 0, 1);
1009 g.add_edge(0, 3, 1);
1010 let result = is_bipartite(&g);
1011 assert!(result.is_some());
1012 }
1013
1014 #[test]
1015 fn test_is_bipartite_no_odd_cycle() {
1016 let mut g = WGraph::new(3);
1018 g.add_edge(0, 1, 1);
1019 g.add_edge(1, 0, 1);
1020 g.add_edge(1, 2, 1);
1021 g.add_edge(2, 1, 1);
1022 g.add_edge(2, 0, 1);
1023 g.add_edge(0, 2, 1);
1024 assert!(is_bipartite(&g).is_none());
1025 }
1026
1027 #[test]
1028 fn test_is_bipartite_empty() {
1029 let g = WGraph::new(3);
1030 let result = is_bipartite(&g);
1031 assert!(result.is_some());
1032 }
1033
1034 #[test]
1037 fn test_chromatic_number_path() {
1038 let mut g = WGraph::new(3);
1040 g.add_edge(0, 1, 1);
1041 g.add_edge(1, 0, 1);
1042 g.add_edge(1, 2, 1);
1043 g.add_edge(2, 1, 1);
1044 let chi = chromatic_number_approx(&g);
1045 assert_eq!(chi, 2);
1046 }
1047
1048 #[test]
1049 fn test_chromatic_number_triangle() {
1050 let mut g = WGraph::new(3);
1052 g.add_edge(0, 1, 1);
1053 g.add_edge(1, 0, 1);
1054 g.add_edge(1, 2, 1);
1055 g.add_edge(2, 1, 1);
1056 g.add_edge(0, 2, 1);
1057 g.add_edge(2, 0, 1);
1058 let chi = chromatic_number_approx(&g);
1059 assert_eq!(chi, 3);
1060 }
1061
1062 #[test]
1063 fn test_chromatic_number_empty() {
1064 let g = WGraph::new(0);
1065 assert_eq!(chromatic_number_approx(&g), 0);
1066 }
1067
1068 #[test]
1069 fn test_chromatic_number_single() {
1070 let g = WGraph::new(1);
1071 assert_eq!(chromatic_number_approx(&g), 1);
1072 }
1073
1074 #[test]
1075 fn test_chromatic_number_independent_set() {
1076 let g = WGraph::new(4);
1078 assert_eq!(chromatic_number_approx(&g), 1);
1079 }
1080
1081 #[test]
1084 fn test_dijkstra_zero_weight() {
1085 let mut g = WGraph::new(3);
1086 g.add_edge(0, 1, 0);
1087 g.add_edge(1, 2, 0);
1088 let dist = dijkstra(&g, 0);
1089 assert_eq!(dist[2], Some(0));
1090 }
1091
1092 #[test]
1093 fn test_kruskal_negative_weights() {
1094 let edges = vec![(0, 1, -5), (1, 2, -3), (0, 2, -1)];
1095 let mst = kruskal_mst(3, &edges);
1096 assert_eq!(mst.total_weight, -8); }
1098
1099 #[test]
1100 fn test_floyd_warshall_self_loop() {
1101 let mut g = WGraph::new(2);
1102 g.add_edge(0, 0, 100);
1103 g.add_edge(0, 1, 5);
1104 let apsp = floyd_warshall(&g);
1105 assert_eq!(apsp.dist[0][0], Some(0));
1106 assert_eq!(apsp.dist[0][1], Some(5));
1107 }
1108}