1#![allow(clippy::needless_range_loop, clippy::ptr_arg)]
6use std::cmp::Ordering;
7use std::collections::{BinaryHeap, VecDeque};
8
9use super::types::{AStarState, Graph, State, UnionFind};
10
11#[allow(dead_code)]
13pub fn bfs(graph: &Graph, start: usize) -> Vec<usize> {
14 let adj = graph.adjacency_list();
15 let mut visited = vec![false; graph.n_nodes];
16 let mut order = Vec::new();
17 let mut queue = VecDeque::new();
18 visited[start] = true;
19 queue.push_back(start);
20 while let Some(node) = queue.pop_front() {
21 order.push(node);
22 for &(nbr, _) in &adj[node] {
23 if !visited[nbr] {
24 visited[nbr] = true;
25 queue.push_back(nbr);
26 }
27 }
28 }
29 order
30}
31#[allow(dead_code)]
33pub fn dfs(graph: &Graph, start: usize) -> Vec<usize> {
34 let adj = graph.adjacency_list();
35 let mut visited = vec![false; graph.n_nodes];
36 let mut order = Vec::new();
37 let mut stack = vec![start];
38 while let Some(node) = stack.pop() {
39 if visited[node] {
40 continue;
41 }
42 visited[node] = true;
43 order.push(node);
44 for &(nbr, _) in adj[node].iter().rev() {
45 if !visited[nbr] {
46 stack.push(nbr);
47 }
48 }
49 }
50 order
51}
52#[allow(dead_code)]
56pub fn connected_components(graph: &Graph) -> Vec<Vec<usize>> {
57 let mut uf = UnionFind::new(graph.n_nodes);
58 for &(a, b, _) in &graph.edges {
59 uf.union(a, b);
60 }
61 let mut comp: std::collections::HashMap<usize, Vec<usize>> = std::collections::HashMap::new();
62 for i in 0..graph.n_nodes {
63 let root = uf.find(i);
64 comp.entry(root).or_default().push(i);
65 }
66 let mut result: Vec<Vec<usize>> = comp.into_values().collect();
67 for c in &mut result {
68 c.sort_unstable();
69 }
70 result.sort_unstable_by_key(|c| c[0]);
71 result
72}
73#[allow(dead_code)]
75pub fn is_connected(graph: &Graph) -> bool {
76 if graph.n_nodes == 0 {
77 return true;
78 }
79 connected_components(graph).len() == 1
80}
81#[allow(dead_code)]
85pub fn topological_sort(graph: &Graph) -> Option<Vec<usize>> {
86 let n = graph.n_nodes;
87 let mut in_degree = vec![0usize; n];
88 let adj = graph.adjacency_list();
89 for node in 0..n {
90 for &(nbr, _) in &adj[node] {
91 in_degree[nbr] += 1;
92 }
93 }
94 let mut queue: VecDeque<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
95 let mut order = Vec::with_capacity(n);
96 while let Some(node) = queue.pop_front() {
97 order.push(node);
98 for &(nbr, _) in &adj[node] {
99 in_degree[nbr] -= 1;
100 if in_degree[nbr] == 0 {
101 queue.push_back(nbr);
102 }
103 }
104 }
105 if order.len() == n { Some(order) } else { None }
106}
107#[allow(dead_code)]
114pub fn dijkstra(graph: &Graph, start: usize) -> Vec<f64> {
115 let adj = graph.adjacency_list();
116 let mut dist = vec![f64::INFINITY; graph.n_nodes];
117 dist[start] = 0.0;
118 let mut heap = BinaryHeap::new();
119 heap.push(State {
120 cost: 0.0,
121 node: start,
122 });
123 while let Some(State { cost, node }) = heap.pop() {
124 if cost > dist[node] {
125 continue;
126 }
127 for &(nbr, w) in &adj[node] {
128 let next = cost + w;
129 if next < dist[nbr] {
130 dist[nbr] = next;
131 heap.push(State {
132 cost: next,
133 node: nbr,
134 });
135 }
136 }
137 }
138 dist
139}
140#[allow(dead_code)]
145pub fn bellman_ford(graph: &Graph, start: usize) -> Option<Vec<f64>> {
146 let n = graph.n_nodes;
147 let mut dist = vec![f64::INFINITY; n];
148 dist[start] = 0.0;
149 for _ in 0..n.saturating_sub(1) {
150 for &(u, v, w) in &graph.edges {
151 if dist[u].is_finite() && dist[u] + w < dist[v] {
152 dist[v] = dist[u] + w;
153 }
154 }
155 }
156 for &(u, v, w) in &graph.edges {
157 if dist[u].is_finite() && dist[u] + w < dist[v] {
158 return None;
159 }
160 }
161 Some(dist)
162}
163#[allow(dead_code)]
169pub fn path_reconstruct(prev: &[Option<usize>], end: usize) -> Vec<usize> {
170 let mut path = Vec::new();
171 let mut cur = end;
172 loop {
173 path.push(cur);
174 match prev[cur] {
175 None => break,
176 Some(p) => cur = p,
177 }
178 }
179 path.reverse();
180 path
181}
182#[allow(dead_code)]
187pub fn kruskal_mst(graph: &Graph) -> Vec<(usize, usize, f64)> {
188 let mut sorted_edges = graph.edges.clone();
189 sorted_edges.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Ordering::Equal));
190 let mut uf = UnionFind::new(graph.n_nodes);
191 let mut mst = Vec::new();
192 for (u, v, w) in sorted_edges {
193 if uf.union(u, v) {
194 mst.push((u, v, w));
195 }
196 }
197 mst
198}
199#[allow(dead_code)]
201pub fn mst_total_weight(mst_edges: &[(usize, usize, f64)]) -> f64 {
202 mst_edges.iter().map(|&(_, _, w)| w).sum()
203}
204#[allow(dead_code)]
209pub fn contact_graph_from_overlaps(overlaps: &[(usize, usize)]) -> Graph {
210 let n = overlaps
211 .iter()
212 .flat_map(|&(a, b)| [a, b])
213 .max()
214 .map(|m| m + 1)
215 .unwrap_or(0);
216 let mut g = Graph::new(n);
217 for &(a, b) in overlaps {
218 g.add_undirected_edge(a, b, 1.0);
219 }
220 g
221}
222#[allow(dead_code)]
224pub fn island_count(graph: &Graph) -> usize {
225 connected_components(graph).len()
226}
227#[allow(dead_code)]
232pub fn laplacian_matrix(graph: &Graph) -> Vec<Vec<f64>> {
233 let n = graph.n_nodes;
234 let mut l = vec![vec![0.0f64; n]; n];
235 for &(u, v, w) in &graph.edges {
236 l[u][v] -= w;
237 l[u][u] += w;
238 }
239 l
240}
241pub fn astar(
246 graph: &Graph,
247 start: usize,
248 goal: usize,
249 heuristic: &dyn Fn(usize) -> f64,
250) -> Option<(f64, Vec<Option<usize>>)> {
251 let adj = graph.adjacency_list();
252 let mut dist = vec![f64::INFINITY; graph.n_nodes];
253 let mut prev: Vec<Option<usize>> = vec![None; graph.n_nodes];
254 dist[start] = 0.0;
255 let mut heap: BinaryHeap<AStarState> = BinaryHeap::new();
256 heap.push(AStarState {
257 f: dist[start] + heuristic(start),
258 g: 0.0,
259 node: start,
260 });
261 while let Some(AStarState { f: _, g, node }) = heap.pop() {
262 if g > dist[node] {
263 continue;
264 }
265 if node == goal {
266 return Some((dist[goal], prev));
267 }
268 for &(nbr, w) in &adj[node] {
269 let new_g = g + w;
270 if new_g < dist[nbr] {
271 dist[nbr] = new_g;
272 prev[nbr] = Some(node);
273 heap.push(AStarState {
274 f: new_g + heuristic(nbr),
275 g: new_g,
276 node: nbr,
277 });
278 }
279 }
280 }
281 if dist[goal].is_finite() {
282 Some((dist[goal], prev))
283 } else {
284 None
285 }
286}
287pub fn floyd_warshall(graph: &Graph) -> Vec<Vec<f64>> {
293 let n = graph.n_nodes;
294 let mut dist = vec![vec![f64::INFINITY; n]; n];
295 for i in 0..n {
296 dist[i][i] = 0.0;
297 }
298 for &(u, v, w) in &graph.edges {
299 if w < dist[u][v] {
300 dist[u][v] = w;
301 }
302 }
303 for k in 0..n {
304 for i in 0..n {
305 for j in 0..n {
306 if dist[i][k].is_finite() && dist[k][j].is_finite() {
307 let via = dist[i][k] + dist[k][j];
308 if via < dist[i][j] {
309 dist[i][j] = via;
310 }
311 }
312 }
313 }
314 }
315 dist
316}
317pub fn kosaraju_scc(graph: &Graph) -> Vec<Vec<usize>> {
322 let n = graph.n_nodes;
323 let adj = graph.adjacency_list();
324 let mut visited = vec![false; n];
325 let mut finish_stack: Vec<usize> = Vec::with_capacity(n);
326 fn dfs1(
327 node: usize,
328 adj: &[Vec<(usize, f64)>],
329 visited: &mut Vec<bool>,
330 stack: &mut Vec<usize>,
331 ) {
332 let mut call_stack = vec![(node, 0usize)];
333 while let Some((v, idx)) = call_stack.last_mut() {
334 let v = *v;
335 if !visited[v] {
336 visited[v] = true;
337 }
338 if *idx < adj[v].len() {
339 let (nbr, _) = adj[v][*idx];
340 *idx += 1;
341 if !visited[nbr] {
342 call_stack.push((nbr, 0));
343 }
344 } else {
345 stack.push(v);
346 call_stack.pop();
347 }
348 }
349 }
350 for i in 0..n {
351 if !visited[i] {
352 dfs1(i, &adj, &mut visited, &mut finish_stack);
353 }
354 }
355 let mut radj: Vec<Vec<(usize, f64)>> = vec![Vec::new(); n];
356 for &(u, v, w) in &graph.edges {
357 radj[v].push((u, w));
358 }
359 let mut visited2 = vec![false; n];
360 let mut sccs: Vec<Vec<usize>> = Vec::new();
361 while let Some(start) = finish_stack.pop() {
362 if visited2[start] {
363 continue;
364 }
365 let mut comp = Vec::new();
366 let mut stack = vec![start];
367 while let Some(v) = stack.pop() {
368 if visited2[v] {
369 continue;
370 }
371 visited2[v] = true;
372 comp.push(v);
373 for &(nbr, _) in &radj[v] {
374 if !visited2[nbr] {
375 stack.push(nbr);
376 }
377 }
378 }
379 comp.sort_unstable();
380 sccs.push(comp);
381 }
382 sccs
383}
384pub fn tarjan_scc(graph: &Graph) -> Vec<Vec<usize>> {
388 let n = graph.n_nodes;
389 let adj = graph.adjacency_list();
390 let mut index = 0usize;
391 let mut indices = vec![usize::MAX; n];
392 let mut lowlinks = vec![0usize; n];
393 let mut on_stack = vec![false; n];
394 let mut stack: Vec<usize> = Vec::new();
395 let mut sccs: Vec<Vec<usize>> = Vec::new();
396 let mut call_stack: Vec<(usize, usize)> = Vec::new();
397 for start in 0..n {
398 if indices[start] != usize::MAX {
399 continue;
400 }
401 call_stack.push((start, 0));
402 while let Some(&mut (v, ref mut adj_idx)) = call_stack.last_mut() {
403 if indices[v] == usize::MAX {
404 indices[v] = index;
405 lowlinks[v] = index;
406 index += 1;
407 stack.push(v);
408 on_stack[v] = true;
409 }
410 let mut pushed = false;
411 while *adj_idx < adj[v].len() {
412 let (w, _) = adj[v][*adj_idx];
413 *adj_idx += 1;
414 if indices[w] == usize::MAX {
415 call_stack.push((w, 0));
416 pushed = true;
417 break;
418 } else if on_stack[w] {
419 lowlinks[v] = lowlinks[v].min(indices[w]);
420 }
421 }
422 if !pushed {
423 let (v, _) = call_stack
424 .pop()
425 .expect("call_stack is non-empty in backtrack");
426 if let Some(&mut (parent, _)) = call_stack.last_mut() {
427 lowlinks[parent] = lowlinks[parent].min(lowlinks[v]);
428 }
429 if lowlinks[v] == indices[v] {
430 let mut comp = Vec::new();
431 loop {
432 let w = stack.pop().expect("stack is non-empty in SCC extraction");
433 on_stack[w] = false;
434 comp.push(w);
435 if w == v {
436 break;
437 }
438 }
439 comp.sort_unstable();
440 sccs.push(comp);
441 }
442 }
443 }
444 }
445 sccs
446}
447pub fn prim_mst(graph: &Graph) -> Vec<(usize, usize, f64)> {
452 if graph.n_nodes == 0 {
453 return Vec::new();
454 }
455 let adj = graph.adjacency_list();
456 let mut in_tree = vec![false; graph.n_nodes];
457 let mut key = vec![f64::INFINITY; graph.n_nodes];
458 let mut parent: Vec<Option<usize>> = vec![None; graph.n_nodes];
459 key[0] = 0.0;
460 for _ in 0..graph.n_nodes {
461 let u = (0..graph.n_nodes)
462 .filter(|&v| !in_tree[v])
463 .min_by(|&a, &b| key[a].partial_cmp(&key[b]).unwrap_or(Ordering::Equal));
464 let u = match u {
465 Some(v) => v,
466 None => break,
467 };
468 in_tree[u] = true;
469 for &(v, w) in &adj[u] {
470 if !in_tree[v] && w < key[v] {
471 key[v] = w;
472 parent[v] = Some(u);
473 }
474 }
475 }
476 let mut mst = Vec::new();
477 for v in 0..graph.n_nodes {
478 if let Some(u) = parent[v] {
479 mst.push((u, v, key[v]));
480 }
481 }
482 mst
483}
484pub fn max_flow_edmonds_karp(graph: &Graph, source: usize, sink: usize) -> f64 {
489 let n = graph.n_nodes;
490 let mut cap = vec![vec![0.0f64; n]; n];
491 for &(u, v, w) in &graph.edges {
492 cap[u][v] += w;
493 }
494 let mut flow = 0.0f64;
495 loop {
496 let mut prev = vec![usize::MAX; n];
497 let mut visited = vec![false; n];
498 visited[source] = true;
499 let mut queue = VecDeque::new();
500 queue.push_back(source);
501 while let Some(u) = queue.pop_front() {
502 if u == sink {
503 break;
504 }
505 for v in 0..n {
506 if !visited[v] && cap[u][v] > 1e-12 {
507 visited[v] = true;
508 prev[v] = u;
509 queue.push_back(v);
510 }
511 }
512 }
513 if !visited[sink] {
514 break;
515 }
516 let mut path_flow = f64::INFINITY;
517 let mut v = sink;
518 while v != source {
519 let u = prev[v];
520 path_flow = path_flow.min(cap[u][v]);
521 v = u;
522 }
523 let mut v = sink;
524 while v != source {
525 let u = prev[v];
526 cap[u][v] -= path_flow;
527 cap[v][u] += path_flow;
528 v = u;
529 }
530 flow += path_flow;
531 }
532 flow
533}
534pub fn dijkstra_with_path(graph: &Graph, start: usize) -> (Vec<f64>, Vec<Option<usize>>) {
539 let adj = graph.adjacency_list();
540 let mut dist = vec![f64::INFINITY; graph.n_nodes];
541 let mut prev: Vec<Option<usize>> = vec![None; graph.n_nodes];
542 dist[start] = 0.0;
543 let mut heap = BinaryHeap::new();
544 heap.push(State {
545 cost: 0.0,
546 node: start,
547 });
548 while let Some(State { cost, node }) = heap.pop() {
549 if cost > dist[node] {
550 continue;
551 }
552 for &(nbr, w) in &adj[node] {
553 let next = cost + w;
554 if next < dist[nbr] {
555 dist[nbr] = next;
556 prev[nbr] = Some(node);
557 heap.push(State {
558 cost: next,
559 node: nbr,
560 });
561 }
562 }
563 }
564 (dist, prev)
565}
566pub fn is_dag(graph: &Graph) -> bool {
568 topological_sort(graph).is_some()
569}
570pub fn count_back_edges(graph: &Graph) -> usize {
574 let adj = graph.adjacency_list();
575 let n = graph.n_nodes;
576 let mut color = vec![0u8; n];
577 let mut back_edges = 0usize;
578 fn dfs_color(v: usize, adj: &[Vec<(usize, f64)>], color: &mut Vec<u8>, back: &mut usize) {
579 color[v] = 1;
580 for &(w, _) in &adj[v] {
581 if color[w] == 1 {
582 *back += 1;
583 } else if color[w] == 0 {
584 dfs_color(w, adj, color, back);
585 }
586 }
587 color[v] = 2;
588 }
589 for i in 0..n {
590 if color[i] == 0 {
591 dfs_color(i, &adj, &mut color, &mut back_edges);
592 }
593 }
594 back_edges
595}
596pub fn graph_diameter_bfs(graph: &Graph) -> usize {
600 let mut diameter = 0usize;
601 for start in 0..graph.n_nodes {
602 let bfs_dist = bfs_distances(graph, start);
603 for d in bfs_dist {
604 if d != usize::MAX && d > diameter {
605 diameter = d;
606 }
607 }
608 }
609 diameter
610}
611pub fn bfs_distances(graph: &Graph, start: usize) -> Vec<usize> {
613 let adj = graph.adjacency_list();
614 let mut dist = vec![usize::MAX; graph.n_nodes];
615 dist[start] = 0;
616 let mut queue = VecDeque::new();
617 queue.push_back(start);
618 while let Some(v) = queue.pop_front() {
619 for &(w, _) in &adj[v] {
620 if dist[w] == usize::MAX {
621 dist[w] = dist[v] + 1;
622 queue.push_back(w);
623 }
624 }
625 }
626 dist
627}
628pub fn degree_centrality(graph: &Graph) -> Vec<f64> {
630 let n = graph.n_nodes;
631 if n <= 1 {
632 return vec![0.0; n];
633 }
634 (0..n)
635 .map(|v| graph.degree(v) as f64 / (n - 1) as f64)
636 .collect()
637}
638pub fn adjacency_matrix(graph: &Graph) -> Vec<Vec<f64>> {
640 let n = graph.n_nodes;
641 let mut mat = vec![vec![0.0f64; n]; n];
642 for &(u, v, w) in &graph.edges {
643 mat[u][v] = w;
644 }
645 mat
646}
647pub fn distance_matrix_to_graph(dist: &[Vec<f64>], threshold: f64) -> Graph {
649 let n = dist.len();
650 let mut g = Graph::new(n);
651 for i in 0..n {
652 for j in 0..n {
653 if i != j && dist[i][j] < threshold {
654 g.add_edge(i, j, dist[i][j]);
655 }
656 }
657 }
658 g
659}
660pub fn is_bipartite(graph: &Graph) -> bool {
665 let adj = graph.adjacency_list();
666 let mut color = vec![usize::MAX; graph.n_nodes];
667 for start in 0..graph.n_nodes {
668 if color[start] != usize::MAX {
669 continue;
670 }
671 color[start] = 0;
672 let mut queue = VecDeque::new();
673 queue.push_back(start);
674 while let Some(u) = queue.pop_front() {
675 for &(v, _w) in &adj[u] {
676 if color[v] == usize::MAX {
677 color[v] = 1 - color[u];
678 queue.push_back(v);
679 } else if color[v] == color[u] {
680 return false;
681 }
682 }
683 }
684 }
685 true
686}
687pub fn bipartite_coloring(graph: &Graph) -> Option<Vec<usize>> {
691 let adj = graph.adjacency_list();
692 let mut color = vec![usize::MAX; graph.n_nodes];
693 for start in 0..graph.n_nodes {
694 if color[start] != usize::MAX {
695 continue;
696 }
697 color[start] = 0;
698 let mut queue = VecDeque::new();
699 queue.push_back(start);
700 while let Some(u) = queue.pop_front() {
701 for &(v, _w) in &adj[u] {
702 if color[v] == usize::MAX {
703 color[v] = 1 - color[u];
704 queue.push_back(v);
705 } else if color[v] == color[u] {
706 return None;
707 }
708 }
709 }
710 }
711 Some(color)
712}
713pub fn greedy_coloring(graph: &Graph) -> Vec<usize> {
722 let adj = graph.adjacency_list();
723 let n = graph.n_nodes;
724 let mut color: Vec<Option<usize>> = vec![None; n];
725 for u in 0..n {
726 let mut used: std::collections::HashSet<usize> = std::collections::HashSet::new();
727 for &(v, _w) in &adj[u] {
728 if let Some(c) = color[v] {
729 used.insert(c);
730 }
731 }
732 let mut c = 0usize;
733 while used.contains(&c) {
734 c += 1;
735 }
736 color[u] = Some(c);
737 }
738 color.into_iter().map(|c| c.unwrap_or(0)).collect()
739}
740pub fn chromatic_number_upper_bound(coloring: &[usize]) -> usize {
742 coloring.iter().copied().max().map(|m| m + 1).unwrap_or(0)
743}
744pub fn kahn_topological_sort(graph: &Graph) -> Option<Vec<usize>> {
749 let n = graph.n_nodes;
750 let mut in_degree = vec![0usize; n];
751 let adj = graph.adjacency_list();
752 for u in 0..n {
753 for &(v, _w) in &adj[u] {
754 in_degree[v] += 1;
755 }
756 }
757 let mut queue: VecDeque<usize> = (0..n).filter(|&u| in_degree[u] == 0).collect();
758 let mut order = Vec::with_capacity(n);
759 while let Some(u) = queue.pop_front() {
760 order.push(u);
761 for &(v, _w) in &adj[u] {
762 in_degree[v] -= 1;
763 if in_degree[v] == 0 {
764 queue.push_back(v);
765 }
766 }
767 }
768 if order.len() == n { Some(order) } else { None }
769}
770pub fn has_eulerian_circuit(graph: &Graph) -> bool {
775 for u in 0..graph.n_nodes {
776 if !graph.degree(u).is_multiple_of(2) {
777 return false;
778 }
779 }
780 let non_isolated: Vec<usize> = (0..graph.n_nodes)
781 .filter(|&u| graph.degree(u) > 0)
782 .collect();
783 if non_isolated.is_empty() {
784 return true;
785 }
786 let start = non_isolated[0];
787 let visited = {
788 let adj = graph.adjacency_list();
789 let mut vis = vec![false; graph.n_nodes];
790 let mut stack = vec![start];
791 vis[start] = true;
792 while let Some(u) = stack.pop() {
793 for &(v, _w) in &adj[u] {
794 if !vis[v] {
795 vis[v] = true;
796 stack.push(v);
797 }
798 }
799 }
800 vis
801 };
802 non_isolated.iter().all(|&u| visited[u])
803}
804pub fn has_eulerian_path(graph: &Graph) -> bool {
808 let odd_degree_count = (0..graph.n_nodes)
809 .filter(|&u| !graph.degree(u).is_multiple_of(2))
810 .count();
811 odd_degree_count == 2 && {
812 let non_isolated: Vec<usize> = (0..graph.n_nodes)
813 .filter(|&u| graph.degree(u) > 0)
814 .collect();
815 if non_isolated.is_empty() {
816 return false;
817 }
818 let start = non_isolated[0];
819 let adj = graph.adjacency_list();
820 let mut vis = vec![false; graph.n_nodes];
821 let mut stack = vec![start];
822 vis[start] = true;
823 while let Some(u) = stack.pop() {
824 for &(v, _w) in &adj[u] {
825 if !vis[v] {
826 vis[v] = true;
827 stack.push(v);
828 }
829 }
830 }
831 non_isolated.iter().all(|&u| vis[u])
832 }
833}
834pub fn betweenness_centrality(graph: &Graph) -> Vec<f64> {
840 let n = graph.n_nodes;
841 let mut bc = vec![0.0f64; n];
842 for s in 0..n {
843 let (_dist, prev) = dijkstra_with_path(graph, s);
844 for t in 0..n {
845 if t == s {
846 continue;
847 }
848 let path = path_reconstruct(&prev, t);
849 if path.len() < 3 {
850 continue;
851 }
852 for &v in &path[1..path.len() - 1] {
853 bc[v] += 1.0;
854 }
855 }
856 }
857 let norm = if n > 2 {
858 ((n - 1) * (n - 2)) as f64
859 } else {
860 1.0
861 };
862 for v in bc.iter_mut() {
863 *v /= norm;
864 }
865 bc
866}
867pub fn closeness_centrality(graph: &Graph) -> Vec<f64> {
872 let n = graph.n_nodes;
873 let mut cc = vec![0.0f64; n];
874 for u in 0..n {
875 let dist = dijkstra(graph, u);
876 let (sum, reachable) = dist
877 .iter()
878 .enumerate()
879 .fold((0.0f64, 0usize), |(s, r), (v, &d)| {
880 if v != u && d.is_finite() {
881 (s + d, r + 1)
882 } else {
883 (s, r)
884 }
885 });
886 if reachable == 0 || sum == 0.0 {
887 cc[u] = 0.0;
888 } else {
889 let raw = (reachable as f64) / sum;
890 cc[u] = raw * (reachable as f64) / ((n - 1) as f64);
891 }
892 }
893 cc
894}
895pub fn bipartite_matching(graph: &Graph, left: &[usize], right: &[usize]) -> Vec<(usize, usize)> {
900 let adj = graph.adjacency_list();
901 let mut right_idx = vec![usize::MAX; graph.n_nodes];
902 for (i, &r) in right.iter().enumerate() {
903 right_idx[r] = i;
904 }
905 let nr = right.len();
906 let mut match_l: Vec<Option<usize>> = vec![None; graph.n_nodes];
907 let mut match_r: Vec<Option<usize>> = vec![None; nr];
908 fn augment(
909 u: usize,
910 adj: &[Vec<(usize, f64)>],
911 right_idx: &[usize],
912 match_r: &mut Vec<Option<usize>>,
913 visited: &mut Vec<bool>,
914 ) -> bool {
915 for &(v, _w) in &adj[u] {
916 let ri = right_idx[v];
917 if ri == usize::MAX || visited[ri] {
918 continue;
919 }
920 visited[ri] = true;
921 let can =
922 match_r[ri].is_none_or(|prev_l| augment(prev_l, adj, right_idx, match_r, visited));
923 if can {
924 match_r[ri] = Some(u);
925 return true;
926 }
927 }
928 false
929 }
930 for &l in left {
931 let mut visited = vec![false; nr];
932 if augment(l, &adj, &right_idx, &mut match_r, &mut visited) {
933 for (ri, mr) in match_r.iter().enumerate() {
934 if *mr == Some(l) {
935 match_l[l] = Some(ri);
936 }
937 }
938 }
939 }
940 let mut pairs = Vec::new();
941 for &l in left {
942 if let Some(ri) = match_l[l] {
943 pairs.push((l, right[ri]));
944 }
945 }
946 pairs
947}
948pub fn graph_density(graph: &Graph) -> f64 {
950 let n = graph.n_nodes;
951 if n <= 1 {
952 return 0.0;
953 }
954 graph.edge_count() as f64 / (n * (n - 1)) as f64
955}
956pub fn local_clustering(graph: &Graph, u: usize) -> f64 {
960 let adj = graph.adjacency_list();
961 let neighbours: Vec<usize> = adj[u].iter().map(|&(v, _)| v).collect();
962 let k = neighbours.len();
963 if k < 2 {
964 return 0.0;
965 }
966 let neighbour_set: std::collections::HashSet<usize> = neighbours.iter().copied().collect();
967 let mut edges_among = 0usize;
968 for &v in &neighbours {
969 for &(w, _) in &adj[v] {
970 if neighbour_set.contains(&w) && w != u {
971 edges_among += 1;
972 }
973 }
974 }
975 edges_among as f64 / (k * (k - 1)) as f64
976}
977pub fn average_clustering(graph: &Graph) -> f64 {
979 if graph.n_nodes == 0 {
980 return 0.0;
981 }
982 let total: f64 = (0..graph.n_nodes).map(|u| local_clustering(graph, u)).sum();
983 total / graph.n_nodes as f64
984}
985#[cfg(test)]
986mod tests {
987 use super::*;
988 fn triangle_graph() -> Graph {
989 let mut g = Graph::new(3);
990 g.add_undirected_edge(0, 1, 1.0);
991 g.add_undirected_edge(1, 2, 2.0);
992 g.add_undirected_edge(0, 2, 4.0);
993 g
994 }
995 #[test]
996 fn test_new_graph() {
997 let g = Graph::new(5);
998 assert_eq!(g.node_count(), 5);
999 assert_eq!(g.edge_count(), 0);
1000 }
1001 #[test]
1002 fn test_add_directed_edge() {
1003 let mut g = Graph::new(3);
1004 g.add_edge(0, 1, 1.5);
1005 assert_eq!(g.edge_count(), 1);
1006 }
1007 #[test]
1008 fn test_add_undirected_edge_stores_two() {
1009 let mut g = Graph::new(3);
1010 g.add_undirected_edge(0, 1, 1.0);
1011 assert_eq!(g.edge_count(), 2);
1012 }
1013 #[test]
1014 fn test_degree() {
1015 let g = triangle_graph();
1016 assert_eq!(g.degree(0), 2);
1017 assert_eq!(g.degree(1), 2);
1018 assert_eq!(g.degree(2), 2);
1019 }
1020 #[test]
1021 fn test_adjacency_list() {
1022 let mut g = Graph::new(3);
1023 g.add_edge(0, 1, 2.0);
1024 g.add_edge(0, 2, 3.0);
1025 let adj = g.adjacency_list();
1026 assert_eq!(adj[0].len(), 2);
1027 assert_eq!(adj[1].len(), 0);
1028 }
1029 #[test]
1030 fn test_bfs_connected() {
1031 let g = triangle_graph();
1032 let order = bfs(&g, 0);
1033 assert_eq!(order.len(), 3);
1034 assert_eq!(order[0], 0);
1035 }
1036 #[test]
1037 fn test_bfs_single_node() {
1038 let g = Graph::new(1);
1039 let order = bfs(&g, 0);
1040 assert_eq!(order, vec![0]);
1041 }
1042 #[test]
1043 fn test_bfs_disconnected_visits_reachable() {
1044 let mut g = Graph::new(4);
1045 g.add_undirected_edge(0, 1, 1.0);
1046 let order = bfs(&g, 0);
1047 assert_eq!(order.len(), 2);
1048 }
1049 #[test]
1050 fn test_dfs_connected() {
1051 let g = triangle_graph();
1052 let order = dfs(&g, 0);
1053 assert_eq!(order.len(), 3);
1054 assert_eq!(order[0], 0);
1055 }
1056 #[test]
1057 fn test_dfs_single_node() {
1058 let g = Graph::new(1);
1059 assert_eq!(dfs(&g, 0), vec![0]);
1060 }
1061 #[test]
1062 fn test_connected_components_single() {
1063 let g = triangle_graph();
1064 let comps = connected_components(&g);
1065 assert_eq!(comps.len(), 1);
1066 assert_eq!(comps[0], vec![0, 1, 2]);
1067 }
1068 #[test]
1069 fn test_connected_components_two() {
1070 let mut g = Graph::new(4);
1071 g.add_undirected_edge(0, 1, 1.0);
1072 g.add_undirected_edge(2, 3, 1.0);
1073 let comps = connected_components(&g);
1074 assert_eq!(comps.len(), 2);
1075 }
1076 #[test]
1077 fn test_is_connected_true() {
1078 assert!(is_connected(&triangle_graph()));
1079 }
1080 #[test]
1081 fn test_is_connected_false() {
1082 let g = Graph::new(3);
1083 assert!(!is_connected(&g));
1084 }
1085 #[test]
1086 fn test_is_connected_single_node() {
1087 let g = Graph::new(1);
1088 assert!(is_connected(&g));
1089 }
1090 #[test]
1091 fn test_topological_sort_dag() {
1092 let mut g = Graph::new(4);
1093 g.add_edge(0, 1, 1.0);
1094 g.add_edge(0, 2, 1.0);
1095 g.add_edge(1, 3, 1.0);
1096 g.add_edge(2, 3, 1.0);
1097 let order = topological_sort(&g).expect("DAG should succeed");
1098 assert_eq!(order.len(), 4);
1099 let pos: Vec<usize> = {
1100 let mut p = vec![0usize; 4];
1101 for (i, &n) in order.iter().enumerate() {
1102 p[n] = i;
1103 }
1104 p
1105 };
1106 assert!(pos[0] < pos[1]);
1107 assert!(pos[0] < pos[2]);
1108 assert!(pos[1] < pos[3]);
1109 }
1110 #[test]
1111 fn test_topological_sort_cycle() {
1112 let mut g = Graph::new(3);
1113 g.add_edge(0, 1, 1.0);
1114 g.add_edge(1, 2, 1.0);
1115 g.add_edge(2, 0, 1.0);
1116 assert!(topological_sort(&g).is_none());
1117 }
1118 #[test]
1119 fn test_dijkstra_triangle() {
1120 let g = triangle_graph();
1121 let dist = dijkstra(&g, 0);
1122 assert_eq!(dist[0], 0.0);
1123 assert_eq!(dist[1], 1.0);
1124 assert_eq!(dist[2], 3.0);
1125 }
1126 #[test]
1127 fn test_dijkstra_unreachable() {
1128 let mut g = Graph::new(3);
1129 g.add_edge(0, 1, 1.0);
1130 let dist = dijkstra(&g, 0);
1131 assert_eq!(dist[2], f64::INFINITY);
1132 }
1133 #[test]
1134 fn test_bellman_ford_no_negative_cycle() {
1135 let g = triangle_graph();
1136 let dist = bellman_ford(&g, 0).expect("no negative cycle");
1137 assert_eq!(dist[0], 0.0);
1138 assert_eq!(dist[1], 1.0);
1139 }
1140 #[test]
1141 fn test_bellman_ford_negative_cycle() {
1142 let mut g = Graph::new(3);
1143 g.add_edge(0, 1, 1.0);
1144 g.add_edge(1, 2, -2.0);
1145 g.add_edge(2, 0, -1.0);
1146 assert!(bellman_ford(&g, 0).is_none());
1147 }
1148 #[test]
1149 fn test_path_reconstruct() {
1150 let prev: Vec<Option<usize>> = vec![None, Some(0), Some(1), Some(2)];
1151 let path = path_reconstruct(&prev, 3);
1152 assert_eq!(path, vec![0, 1, 2, 3]);
1153 }
1154 #[test]
1155 fn test_path_reconstruct_single() {
1156 let prev: Vec<Option<usize>> = vec![None];
1157 assert_eq!(path_reconstruct(&prev, 0), vec![0]);
1158 }
1159 #[test]
1160 fn test_kruskal_mst_triangle() {
1161 let g = triangle_graph();
1162 let mst = kruskal_mst(&g);
1163 assert_eq!(mst.len(), 2);
1164 let total = mst_total_weight(&mst);
1165 assert!((total - 3.0).abs() < 1e-12);
1166 }
1167 #[test]
1168 fn test_mst_total_weight_empty() {
1169 assert_eq!(mst_total_weight(&[]), 0.0);
1170 }
1171 #[test]
1172 fn test_contact_graph_from_overlaps() {
1173 let overlaps = vec![(0, 1), (1, 2), (0, 2)];
1174 let g = contact_graph_from_overlaps(&overlaps);
1175 assert_eq!(g.node_count(), 3);
1176 assert_eq!(g.edge_count(), 6);
1177 }
1178 #[test]
1179 fn test_contact_graph_empty() {
1180 let g = contact_graph_from_overlaps(&[]);
1181 assert_eq!(g.node_count(), 0);
1182 }
1183 #[test]
1184 fn test_island_count_one() {
1185 assert_eq!(island_count(&triangle_graph()), 1);
1186 }
1187 #[test]
1188 fn test_island_count_three() {
1189 let g = Graph::new(3);
1190 assert_eq!(island_count(&g), 3);
1191 }
1192 #[test]
1193 fn test_laplacian_row_sum_zero() {
1194 let g = triangle_graph();
1195 let l = laplacian_matrix(&g);
1196 for row in &l {
1197 let s: f64 = row.iter().sum();
1198 assert!(s.abs() < 1e-12, "row sum = {s}");
1199 }
1200 }
1201 #[test]
1202 fn test_laplacian_diagonal_equals_degree() {
1203 let g = triangle_graph();
1204 let l = laplacian_matrix(&g);
1205 assert!((l[0][0] - 5.0).abs() < 1e-12);
1206 assert!((l[1][1] - 3.0).abs() < 1e-12);
1207 assert!((l[2][2] - 6.0).abs() < 1e-12);
1208 }
1209 #[test]
1210 fn test_laplacian_off_diagonal_negative() {
1211 let g = triangle_graph();
1212 let l = laplacian_matrix(&g);
1213 assert!(l[0][1] < 0.0);
1214 assert!(l[1][0] < 0.0);
1215 }
1216 #[test]
1217 fn test_floyd_warshall_triangle() {
1218 let g = triangle_graph();
1219 let dist = floyd_warshall(&g);
1220 assert!((dist[0][1] - 1.0).abs() < 1e-12);
1221 assert!((dist[0][2] - 3.0).abs() < 1e-12);
1222 }
1223 #[test]
1224 fn test_floyd_warshall_diagonal_zero() {
1225 let g = triangle_graph();
1226 let dist = floyd_warshall(&g);
1227 for i in 0..3 {
1228 assert!(
1229 (dist[i][i] - 0.0).abs() < 1e-12,
1230 "dist[{}][{}]={}",
1231 i,
1232 i,
1233 dist[i][i]
1234 );
1235 }
1236 }
1237 #[test]
1238 fn test_floyd_warshall_unreachable() {
1239 let mut g = Graph::new(3);
1240 g.add_edge(0, 1, 1.0);
1241 let dist = floyd_warshall(&g);
1242 assert_eq!(dist[0][2], f64::INFINITY);
1243 }
1244 #[test]
1245 fn test_astar_finds_path() {
1246 let g = triangle_graph();
1247 let result = astar(&g, 0, 2, &|_| 0.0);
1248 assert!(result.is_some(), "A* should find a path");
1249 let (cost, _) = result.unwrap();
1250 assert!((cost - 3.0).abs() < 1e-12, "cost={}", cost);
1251 }
1252 #[test]
1253 fn test_astar_no_path() {
1254 let mut g = Graph::new(3);
1255 g.add_edge(0, 1, 1.0);
1256 let result = astar(&g, 0, 2, &|_| 0.0);
1257 assert!(
1258 result.is_none(),
1259 "A* should not find path to unreachable node"
1260 );
1261 }
1262 #[test]
1263 fn test_kosaraju_scc_single_cycle() {
1264 let mut g = Graph::new(3);
1265 g.add_edge(0, 1, 1.0);
1266 g.add_edge(1, 2, 1.0);
1267 g.add_edge(2, 0, 1.0);
1268 let sccs = kosaraju_scc(&g);
1269 assert_eq!(sccs.len(), 1, "one SCC expected for a cycle");
1270 }
1271 #[test]
1272 fn test_kosaraju_scc_dag() {
1273 let mut g = Graph::new(3);
1274 g.add_edge(0, 1, 1.0);
1275 g.add_edge(1, 2, 1.0);
1276 let sccs = kosaraju_scc(&g);
1277 assert_eq!(sccs.len(), 3, "DAG should have 3 SCCs");
1278 }
1279 #[test]
1280 fn test_prim_mst_triangle() {
1281 let g = triangle_graph();
1282 let mst = prim_mst(&g);
1283 let total = mst.iter().map(|&(_, _, w)| w).sum::<f64>();
1284 assert!((total - 3.0).abs() < 1e-12, "prim MST weight={}", total);
1285 }
1286 #[test]
1287 fn test_prim_mst_empty_graph() {
1288 let g = Graph::new(0);
1289 let mst = prim_mst(&g);
1290 assert!(mst.is_empty());
1291 }
1292 #[test]
1293 fn test_max_flow_simple_path() {
1294 let mut g = Graph::new(3);
1295 g.add_edge(0, 1, 3.0);
1296 g.add_edge(1, 2, 2.0);
1297 let flow = max_flow_edmonds_karp(&g, 0, 2);
1298 assert!((flow - 2.0).abs() < 1e-9, "flow={}", flow);
1299 }
1300 #[test]
1301 fn test_max_flow_two_parallel_paths() {
1302 let mut g = Graph::new(4);
1303 g.add_edge(0, 1, 2.0);
1304 g.add_edge(1, 3, 2.0);
1305 g.add_edge(0, 2, 1.0);
1306 g.add_edge(2, 3, 1.0);
1307 let flow = max_flow_edmonds_karp(&g, 0, 3);
1308 assert!((flow - 3.0).abs() < 1e-9, "flow={}", flow);
1309 }
1310 #[test]
1311 fn test_dijkstra_with_path_triangle() {
1312 let g = triangle_graph();
1313 let (dist, prev) = dijkstra_with_path(&g, 0);
1314 assert!((dist[2] - 3.0).abs() < 1e-12);
1315 let path = path_reconstruct(&prev, 2);
1316 assert_eq!(path[0], 0);
1317 assert_eq!(*path.last().unwrap(), 2);
1318 }
1319 #[test]
1320 fn test_is_dag_true() {
1321 let mut g = Graph::new(3);
1322 g.add_edge(0, 1, 1.0);
1323 g.add_edge(1, 2, 1.0);
1324 assert!(is_dag(&g));
1325 }
1326 #[test]
1327 fn test_is_dag_false() {
1328 let mut g = Graph::new(3);
1329 g.add_edge(0, 1, 1.0);
1330 g.add_edge(1, 2, 1.0);
1331 g.add_edge(2, 0, 1.0);
1332 assert!(!is_dag(&g));
1333 }
1334 #[test]
1335 fn test_degree_centrality_triangle() {
1336 let g = triangle_graph();
1337 let dc = degree_centrality(&g);
1338 for c in &dc {
1339 assert!((*c - 1.0).abs() < 1e-12, "c={}", c);
1340 }
1341 }
1342 #[test]
1343 fn test_adjacency_matrix_triangle() {
1344 let mut g = Graph::new(3);
1345 g.add_edge(0, 1, 2.0);
1346 g.add_edge(1, 2, 3.0);
1347 let mat = adjacency_matrix(&g);
1348 assert!((mat[0][1] - 2.0).abs() < 1e-12);
1349 assert!((mat[1][2] - 3.0).abs() < 1e-12);
1350 assert!((mat[0][0]).abs() < 1e-12);
1351 }
1352 #[test]
1353 fn test_graph_diameter_triangle() {
1354 let g = triangle_graph();
1355 let d = graph_diameter_bfs(&g);
1356 assert_eq!(d, 1, "diameter of complete triangle = 1");
1357 }
1358 #[test]
1359 fn test_count_back_edges_cycle() {
1360 let mut g = Graph::new(3);
1361 g.add_edge(0, 1, 1.0);
1362 g.add_edge(1, 2, 1.0);
1363 g.add_edge(2, 0, 1.0);
1364 let back = count_back_edges(&g);
1365 assert!(back > 0, "cycle should have back edges");
1366 }
1367 #[test]
1368 fn test_count_back_edges_dag() {
1369 let mut g = Graph::new(3);
1370 g.add_edge(0, 1, 1.0);
1371 g.add_edge(1, 2, 1.0);
1372 let back = count_back_edges(&g);
1373 assert_eq!(back, 0, "DAG should have no back edges");
1374 }
1375 #[test]
1376 fn test_is_bipartite_path_graph() {
1377 let mut g = Graph::new(4);
1378 g.add_undirected_edge(0, 1, 1.0);
1379 g.add_undirected_edge(1, 2, 1.0);
1380 g.add_undirected_edge(2, 3, 1.0);
1381 assert!(is_bipartite(&g));
1382 }
1383 #[test]
1384 fn test_is_bipartite_odd_cycle_false() {
1385 let g = triangle_graph();
1386 assert!(!is_bipartite(&g));
1387 }
1388 #[test]
1389 fn test_bipartite_coloring_consistent() {
1390 let mut g = Graph::new(4);
1391 g.add_undirected_edge(0, 1, 1.0);
1392 g.add_undirected_edge(1, 2, 1.0);
1393 g.add_undirected_edge(2, 3, 1.0);
1394 let colors = bipartite_coloring(&g).expect("should be bipartite");
1395 assert_ne!(colors[0], colors[1]);
1396 assert_ne!(colors[1], colors[2]);
1397 assert_ne!(colors[2], colors[3]);
1398 }
1399 #[test]
1400 fn test_bipartite_coloring_none_for_triangle() {
1401 assert!(bipartite_coloring(&triangle_graph()).is_none());
1402 }
1403 #[test]
1404 fn test_greedy_coloring_triangle_uses_3_colors() {
1405 let g = triangle_graph();
1406 let colors = greedy_coloring(&g);
1407 assert_eq!(colors.len(), 3);
1408 let k = chromatic_number_upper_bound(&colors);
1409 assert!(k >= 3, "K3 needs at least 3 colours, got {k}");
1410 }
1411 #[test]
1412 fn test_greedy_coloring_adjacent_different() {
1413 let mut g = Graph::new(4);
1414 g.add_undirected_edge(0, 1, 1.0);
1415 g.add_undirected_edge(1, 2, 1.0);
1416 g.add_undirected_edge(2, 3, 1.0);
1417 let colors = greedy_coloring(&g);
1418 assert_ne!(colors[0], colors[1]);
1419 assert_ne!(colors[1], colors[2]);
1420 assert_ne!(colors[2], colors[3]);
1421 }
1422 #[test]
1423 fn test_greedy_coloring_path_needs_2() {
1424 let mut g = Graph::new(4);
1425 g.add_undirected_edge(0, 1, 1.0);
1426 g.add_undirected_edge(1, 2, 1.0);
1427 g.add_undirected_edge(2, 3, 1.0);
1428 let colors = greedy_coloring(&g);
1429 let k = chromatic_number_upper_bound(&colors);
1430 assert!(k <= 2, "path needs at most 2 colours, got {k}");
1431 }
1432 #[test]
1433 fn test_kahn_dag() {
1434 let mut g = Graph::new(4);
1435 g.add_edge(0, 1, 1.0);
1436 g.add_edge(0, 2, 1.0);
1437 g.add_edge(1, 3, 1.0);
1438 g.add_edge(2, 3, 1.0);
1439 let order = kahn_topological_sort(&g).expect("DAG should succeed");
1440 assert_eq!(order.len(), 4);
1441 let pos: Vec<usize> = {
1442 let mut p = vec![0usize; 4];
1443 for (i, &n) in order.iter().enumerate() {
1444 p[n] = i;
1445 }
1446 p
1447 };
1448 assert!(pos[0] < pos[1]);
1449 assert!(pos[0] < pos[2]);
1450 assert!(pos[1] < pos[3]);
1451 }
1452 #[test]
1453 fn test_kahn_cycle_returns_none() {
1454 let mut g = Graph::new(3);
1455 g.add_edge(0, 1, 1.0);
1456 g.add_edge(1, 2, 1.0);
1457 g.add_edge(2, 0, 1.0);
1458 assert!(kahn_topological_sort(&g).is_none());
1459 }
1460 #[test]
1461 fn test_eulerian_circuit_even_degrees() {
1462 let mut g = Graph::new(4);
1463 g.add_undirected_edge(0, 1, 1.0);
1464 g.add_undirected_edge(1, 2, 1.0);
1465 g.add_undirected_edge(2, 3, 1.0);
1466 g.add_undirected_edge(3, 0, 1.0);
1467 assert!(
1468 has_eulerian_circuit(&g),
1469 "square graph should have Eulerian circuit"
1470 );
1471 }
1472 #[test]
1473 fn test_no_eulerian_circuit_triangle() {
1474 let g = triangle_graph();
1475 assert!(has_eulerian_circuit(&g));
1476 }
1477 #[test]
1478 fn test_eulerian_path_odd_degree_nodes() {
1479 let mut g = Graph::new(3);
1480 g.add_edge(0, 1, 1.0);
1481 g.add_edge(1, 2, 1.0);
1482 let odd_count = (0..3).filter(|&u| !g.degree(u).is_multiple_of(2)).count();
1483 assert_eq!(odd_count, 2);
1484 }
1485 #[test]
1486 fn test_betweenness_centrality_path() {
1487 let mut g = Graph::new(3);
1488 g.add_edge(0, 1, 1.0);
1489 g.add_edge(1, 2, 1.0);
1490 let bc = betweenness_centrality(&g);
1491 assert!(bc[1] > bc[0], "bc[1]={}, bc[0]={}", bc[1], bc[0]);
1492 assert!(bc[1] > bc[2], "bc[1]={}, bc[2]={}", bc[1], bc[2]);
1493 }
1494 #[test]
1495 fn test_betweenness_centrality_all_finite() {
1496 let g = triangle_graph();
1497 let bc = betweenness_centrality(&g);
1498 assert!(bc.iter().all(|v| v.is_finite()));
1499 }
1500 #[test]
1501 fn test_closeness_centrality_central_node() {
1502 let mut g = Graph::new(4);
1503 g.add_edge(0, 1, 1.0);
1504 g.add_edge(0, 2, 1.0);
1505 g.add_edge(0, 3, 1.0);
1506 let cc = closeness_centrality(&g);
1507 assert!(cc[0] >= cc[1], "cc[0]={} cc[1]={}", cc[0], cc[1]);
1508 assert!(cc[0] >= cc[2], "cc[0]={} cc[2]={}", cc[0], cc[2]);
1509 }
1510 #[test]
1511 fn test_graph_density_complete_directed() {
1512 let mut g = Graph::new(3);
1513 for i in 0..3 {
1514 for j in 0..3 {
1515 if i != j {
1516 g.add_edge(i, j, 1.0);
1517 }
1518 }
1519 }
1520 let d = graph_density(&g);
1521 assert!((d - 1.0).abs() < 1e-12, "density={d}");
1522 }
1523 #[test]
1524 fn test_graph_density_empty() {
1525 let g = Graph::new(4);
1526 assert_eq!(graph_density(&g), 0.0);
1527 }
1528 #[test]
1529 fn test_bipartite_matching_simple() {
1530 let mut g = Graph::new(4);
1531 g.add_edge(0, 2, 1.0);
1532 g.add_edge(0, 3, 1.0);
1533 g.add_edge(1, 3, 1.0);
1534 let matching = bipartite_matching(&g, &[0, 1], &[2, 3]);
1535 assert_eq!(matching.len(), 2, "matching={:?}", matching);
1536 }
1537 #[test]
1538 fn test_bipartite_matching_no_edges() {
1539 let g = Graph::new(4);
1540 let matching = bipartite_matching(&g, &[0, 1], &[2, 3]);
1541 assert!(matching.is_empty());
1542 }
1543 #[test]
1544 fn test_local_clustering_complete_triangle() {
1545 let g = triangle_graph();
1546 let c = local_clustering(&g, 0);
1547 assert!((c - 1.0).abs() < 1e-10, "clustering={c}");
1548 }
1549 #[test]
1550 fn test_average_clustering_path_graph() {
1551 let mut g = Graph::new(3);
1552 g.add_undirected_edge(0, 1, 1.0);
1553 g.add_undirected_edge(1, 2, 1.0);
1554 let avg = average_clustering(&g);
1555 assert!(avg.is_finite() && avg >= 0.0);
1556 }
1557 #[test]
1558 fn test_dijkstra_single_node() {
1559 let g = Graph::new(1);
1560 let d = dijkstra(&g, 0);
1561 assert_eq!(d.len(), 1);
1562 assert_eq!(d[0], 0.0);
1563 }
1564 #[test]
1565 fn test_dijkstra_self_loop_ignored() {
1566 let mut g = Graph::new(2);
1567 g.add_edge(0, 0, 5.0);
1568 g.add_edge(0, 1, 2.0);
1569 let d = dijkstra(&g, 0);
1570 assert_eq!(d[0], 0.0);
1571 assert_eq!(d[1], 2.0);
1572 }
1573 #[test]
1574 fn test_bellman_ford_unreachable() {
1575 let mut g = Graph::new(3);
1576 g.add_edge(0, 1, 2.0);
1577 let d = bellman_ford(&g, 0).unwrap();
1578 assert!(d[2].is_infinite());
1579 }
1580 #[test]
1581 fn test_astar_same_result_as_dijkstra() {
1582 let g = triangle_graph();
1583 let dijk = dijkstra(&g, 0);
1584 let zero_h = |_: usize| 0.0;
1585 if let Some((d, _)) = astar(&g, 0, 2, &zero_h) {
1586 assert!((d - dijk[2]).abs() < 1e-12, "A* and Dijkstra disagree");
1587 }
1588 }
1589 #[test]
1590 fn test_floyd_warshall_symmetric_undirected() {
1591 let g = triangle_graph();
1592 let dist = floyd_warshall(&g);
1593 for i in 0..3 {
1594 for j in 0..3 {
1595 assert!((dist[i][j] - dist[j][i]).abs() < 1e-10);
1596 }
1597 }
1598 }
1599 #[test]
1600 fn test_topological_sort_empty_graph() {
1601 let g = Graph::new(0);
1602 let order = topological_sort(&g).expect("empty graph is a DAG");
1603 assert!(order.is_empty());
1604 }
1605 #[test]
1606 fn test_tarjan_single_scc() {
1607 let mut g = Graph::new(3);
1608 g.add_edge(0, 1, 1.0);
1609 g.add_edge(1, 2, 1.0);
1610 g.add_edge(2, 0, 1.0);
1611 let sccs = tarjan_scc(&g);
1612 assert_eq!(sccs.len(), 1);
1613 assert_eq!(sccs[0].len(), 3);
1614 }
1615 #[test]
1616 fn test_tarjan_dag_each_node_own_scc() {
1617 let mut g = Graph::new(3);
1618 g.add_edge(0, 1, 1.0);
1619 g.add_edge(1, 2, 1.0);
1620 let sccs = tarjan_scc(&g);
1621 assert_eq!(sccs.len(), 3, "DAG: each node is its own SCC");
1622 }
1623 #[test]
1624 fn test_kosaraju_multiple_sccs() {
1625 let mut g = Graph::new(4);
1626 g.add_edge(0, 1, 1.0);
1627 g.add_edge(1, 0, 1.0);
1628 g.add_edge(2, 3, 1.0);
1629 g.add_edge(3, 2, 1.0);
1630 let sccs = kosaraju_scc(&g);
1631 assert_eq!(sccs.len(), 2);
1632 }
1633 #[test]
1634 fn test_kruskal_prim_agree() {
1635 let mut g = Graph::new(4);
1636 g.add_undirected_edge(0, 1, 2.0);
1637 g.add_undirected_edge(1, 2, 3.0);
1638 g.add_undirected_edge(2, 3, 1.0);
1639 g.add_undirected_edge(0, 3, 4.0);
1640 let w_k = mst_total_weight(&kruskal_mst(&g));
1641 let w_p: f64 = prim_mst(&g).iter().map(|&(_, _, w)| w).sum();
1642 assert!((w_k - w_p).abs() < 1e-10, "Kruskal={w_k} Prim={w_p}");
1643 }
1644 #[test]
1645 fn test_max_flow_new_parallel() {
1646 let mut g = Graph::new(4);
1647 g.add_edge(0, 1, 3.0);
1648 g.add_edge(0, 2, 2.0);
1649 g.add_edge(1, 3, 2.0);
1650 g.add_edge(2, 3, 3.0);
1651 let flow = max_flow_edmonds_karp(&g, 0, 3);
1652 assert!((flow - 4.0).abs() < 1e-9, "flow={flow}");
1653 }
1654 #[test]
1655 fn test_max_flow_no_path() {
1656 let g = Graph::new(3);
1657 let flow = max_flow_edmonds_karp(&g, 0, 2);
1658 assert_eq!(flow, 0.0);
1659 }
1660 #[test]
1661 fn test_max_flow_bottleneck() {
1662 let mut g = Graph::new(3);
1663 g.add_edge(0, 1, 5.0);
1664 g.add_edge(1, 2, 2.0);
1665 let flow = max_flow_edmonds_karp(&g, 0, 2);
1666 assert!((flow - 2.0).abs() < 1e-9, "flow limited by bottleneck=2");
1667 }
1668 #[test]
1669 fn test_is_dag_linear_chain() {
1670 let mut g = Graph::new(3);
1671 g.add_edge(0, 1, 1.0);
1672 g.add_edge(1, 2, 1.0);
1673 assert!(is_dag(&g));
1674 }
1675 #[test]
1676 fn test_is_dag_three_cycle() {
1677 let mut g = Graph::new(3);
1678 g.add_edge(0, 1, 1.0);
1679 g.add_edge(1, 2, 1.0);
1680 g.add_edge(2, 0, 1.0);
1681 assert!(!is_dag(&g));
1682 }
1683 #[test]
1684 fn test_count_back_edges_linear() {
1685 let mut g = Graph::new(3);
1686 g.add_edge(0, 1, 1.0);
1687 g.add_edge(0, 2, 1.0);
1688 assert_eq!(count_back_edges(&g), 0);
1689 }
1690 #[test]
1691 fn test_count_back_edges_two_cycle() {
1692 let mut g = Graph::new(2);
1693 g.add_edge(0, 1, 1.0);
1694 g.add_edge(1, 0, 1.0);
1695 assert!(count_back_edges(&g) >= 1);
1696 }
1697 #[test]
1698 fn test_path_reconstruct_chain() {
1699 let prev: Vec<Option<usize>> = vec![None, Some(0), Some(1)];
1700 let path = path_reconstruct(&prev, 2);
1701 assert_eq!(path, vec![0, 1, 2]);
1702 }
1703 #[test]
1704 fn test_method_scc_single_cycle() {
1705 let mut g = Graph::new(3);
1706 g.add_edge(0, 1, 1.0);
1707 g.add_edge(1, 2, 1.0);
1708 g.add_edge(2, 0, 1.0);
1709 let sccs = g.strongly_connected_components();
1710 assert_eq!(sccs.len(), 1, "expected 1 SCC for a 3-cycle");
1711 let mut flat: Vec<usize> = sccs.into_iter().flatten().collect();
1712 flat.sort_unstable();
1713 assert_eq!(flat, vec![0, 1, 2]);
1714 }
1715 #[test]
1716 fn test_method_scc_dag_all_singleton() {
1717 let mut g = Graph::new(4);
1718 g.add_edge(0, 1, 1.0);
1719 g.add_edge(1, 2, 1.0);
1720 g.add_edge(2, 3, 1.0);
1721 let sccs = g.strongly_connected_components();
1722 assert_eq!(sccs.len(), 4);
1723 }
1724 #[test]
1725 fn test_method_mst_triangle() {
1726 let mut g = Graph::new(3);
1727 g.add_undirected_edge(0, 1, 1.0);
1728 g.add_undirected_edge(1, 2, 2.0);
1729 g.add_undirected_edge(0, 2, 5.0);
1730 let mst = g.minimum_spanning_tree();
1731 assert_eq!(mst.len(), 2);
1732 let total: f64 = mst.iter().map(|&(_, _, w)| w).sum();
1733 assert!((total - 3.0).abs() < 1e-9, "MST weight={}", total);
1734 }
1735 #[test]
1736 fn test_method_mst_star_graph() {
1737 let mut g = Graph::new(4);
1738 g.add_undirected_edge(0, 1, 1.0);
1739 g.add_undirected_edge(0, 2, 1.0);
1740 g.add_undirected_edge(0, 3, 1.0);
1741 let mst = g.minimum_spanning_tree();
1742 assert_eq!(mst.len(), 3);
1743 }
1744 #[test]
1745 fn test_method_max_flow_simple() {
1746 let mut g = Graph::new(3);
1747 g.add_edge(0, 1, 10.0);
1748 g.add_edge(1, 2, 5.0);
1749 let flow = g.max_flow(0, 2);
1750 assert!((flow - 5.0).abs() < 1e-9, "flow limited by bottleneck=5");
1751 }
1752 #[test]
1753 fn test_method_max_flow_no_path() {
1754 let g = Graph::new(4);
1755 let flow = g.max_flow(0, 3);
1756 assert_eq!(flow, 0.0);
1757 }
1758 #[test]
1759 fn test_method_betweenness_path_graph() {
1760 let mut g = Graph::new(4);
1761 g.add_edge(0, 1, 1.0);
1762 g.add_edge(1, 2, 1.0);
1763 g.add_edge(2, 3, 1.0);
1764 let bc = g.betweenness_centrality();
1765 assert!(bc[0].abs() < 1e-9, "bc[0]={}", bc[0]);
1766 assert!(bc[3].abs() < 1e-9, "bc[3]={}", bc[3]);
1767 assert!(bc[1] > 0.0, "bc[1]={}", bc[1]);
1768 assert!(bc[2] > 0.0, "bc[2]={}", bc[2]);
1769 }
1770 #[test]
1771 fn test_method_betweenness_single_node() {
1772 let g = Graph::new(1);
1773 let bc = g.betweenness_centrality();
1774 assert_eq!(bc.len(), 1);
1775 assert_eq!(bc[0], 0.0);
1776 }
1777 #[test]
1778 fn test_method_betweenness_hub_high_centrality() {
1779 let mut g = Graph::new(5);
1780 for i in 1..5 {
1781 g.add_undirected_edge(0, i, 1.0);
1782 }
1783 let bc = g.betweenness_centrality();
1784 for i in 1..5 {
1785 assert!(bc[0] >= bc[i], "hub bc={} leaf[{}] bc={}", bc[0], i, bc[i]);
1786 }
1787 }
1788}