#![allow(clippy::disallowed_methods)]
use aprender::graph::Graph;
fn main() {
println!("═══════════════════════════════════════════════════════════════");
println!(" Comprehensive Graph Algorithms Demo - Aprender v0.6.0");
println!("═══════════════════════════════════════════════════════════════\n");
demo_pathfinding();
demo_components_traversal();
demo_community_link_analysis();
println!("═══════════════════════════════════════════════════════════════");
println!(" Demo Complete! All 11 algorithms demonstrated.");
println!("═══════════════════════════════════════════════════════════════");
}
fn demo_pathfinding() {
println!("╔═══════════════════════════════════════════════════════════════╗");
println!("║ PHASE 1: PATHFINDING ALGORITHMS ║");
println!("╚═══════════════════════════════════════════════════════════════╝\n");
println!("📍 Building road network graph:");
println!(" Cities: A(0), B(1), C(2), D(3), E(4), F(5)");
println!(" Roads with distances (km):\n");
let weighted_edges = vec![
(0, 1, 4.0), (0, 2, 2.0), (1, 2, 1.0), (1, 3, 5.0), (2, 3, 8.0), (2, 4, 10.0), (3, 4, 2.0), (3, 5, 6.0), (4, 5, 3.0), ];
let cities = vec!["A", "B", "C", "D", "E", "F"];
let g_weighted = Graph::from_weighted_edges(&weighted_edges, false);
let unweighted_edges: Vec<(usize, usize)> =
weighted_edges.iter().map(|(u, v, _)| (*u, *v)).collect();
let g_unweighted = Graph::from_edges(&unweighted_edges, false);
println!("─────────────────────────────────────────────────────────────────");
println!("1️⃣ Shortest Path (BFS - unweighted hops)");
println!("─────────────────────────────────────────────────────────────────");
let path = g_unweighted.shortest_path(0, 5).expect("Path should exist");
print!(" Route from {} to {}: ", cities[0], cities[5]);
for (i, &node) in path.iter().enumerate() {
if i > 0 {
print!(" → ");
}
print!("{}", cities[node]);
}
println!(" ({} hops)\n", path.len() - 1);
println!("─────────────────────────────────────────────────────────────────");
println!("2️⃣ Dijkstra's Algorithm (weighted shortest path)");
println!("─────────────────────────────────────────────────────────────────");
let (dijkstra_path, distance) = g_weighted.dijkstra(0, 5).expect("Path should exist");
print!(" Shortest route from {} to {}: ", cities[0], cities[5]);
for (i, &node) in dijkstra_path.iter().enumerate() {
if i > 0 {
print!(" → ");
}
print!("{}", cities[node]);
}
println!(" ({distance:.1} km)\n");
println!("─────────────────────────────────────────────────────────────────");
println!("3️⃣ A* Search (heuristic-guided pathfinding)");
println!("─────────────────────────────────────────────────────────────────");
let heuristic = |node: usize| match node {
0 => 10.0, 1 => 8.0, 2 => 9.0, 3 => 5.0, 4 => 3.0, _ => 0.0, };
let astar_path = g_weighted
.a_star(0, 5, heuristic)
.expect("Path should exist");
print!(" A* route from {} to {}: ", cities[0], cities[5]);
for (i, &node) in astar_path.iter().enumerate() {
if i > 0 {
print!(" → ");
}
print!("{}", cities[node]);
}
println!(" (heuristic-guided)\n");
println!("─────────────────────────────────────────────────────────────────");
println!("4️⃣ All-Pairs Shortest Paths (distance matrix)");
println!("─────────────────────────────────────────────────────────────────");
let dist_matrix = g_unweighted.all_pairs_shortest_paths();
println!(" Distance matrix (hops):");
print!(" ");
for city in &cities {
print!("{city:>3} ");
}
println!();
for (i, row) in dist_matrix.iter().enumerate() {
print!(" {:>2} ", cities[i]);
for dist in row {
match dist {
Some(d) => print!("{d:>3} "),
None => print!(" - "),
}
}
println!();
}
println!();
}
#[allow(clippy::too_many_lines)]
fn demo_components_traversal() {
println!("\n╔═══════════════════════════════════════════════════════════════╗");
println!("║ PHASE 2: COMPONENTS & TRAVERSAL ALGORITHMS ║");
println!("╚═══════════════════════════════════════════════════════════════╝\n");
println!("─────────────────────────────────────────────────────────────────");
println!("1️⃣ Depth-First Search (DFS)");
println!("─────────────────────────────────────────────────────────────────");
let tree_edges = vec![(0, 1), (0, 2), (1, 3), (1, 4), (2, 5)];
let tree = Graph::from_edges(&tree_edges, false);
println!(" Tree structure:");
println!(" 0");
println!(" / \\");
println!(" 1 2");
println!(" / \\ \\");
println!(" 3 4 5\n");
let dfs_order = tree.dfs(0).expect("DFS from root");
print!(" DFS traversal from root: ");
for (i, &node) in dfs_order.iter().enumerate() {
if i > 0 {
print!(" → ");
}
print!("{node}");
}
println!("\n");
println!("─────────────────────────────────────────────────────────────────");
println!("2️⃣ Connected Components (undirected graphs)");
println!("─────────────────────────────────────────────────────────────────");
let component_edges = vec![
(0, 1),
(1, 2), (3, 4), ];
let g_components = Graph::from_edges(&component_edges, false);
let components = g_components.connected_components();
use std::collections::HashMap;
let mut comp_groups: HashMap<usize, Vec<usize>> = HashMap::new();
for (node, &comp_id) in components.iter().enumerate().take(6) {
comp_groups.entry(comp_id).or_default().push(node);
}
println!(" Graph with {} nodes, {} edges", 6, component_edges.len());
println!(" Found {} connected components:", comp_groups.len());
for (i, nodes) in comp_groups.values().enumerate() {
println!(" Component {}: {:?}", i + 1, nodes);
}
println!();
println!("─────────────────────────────────────────────────────────────────");
println!("3️⃣ Strongly Connected Components (directed graphs)");
println!("─────────────────────────────────────────────────────────────────");
let scc_edges = vec![
(0, 1),
(1, 2),
(2, 0), (2, 3),
(3, 4),
(4, 3), ];
let g_directed = Graph::from_edges(&scc_edges, true);
println!(" Directed graph:");
println!(" 0 → 1 → 2 → 0 (cycle 1)");
println!(" ↓");
println!(" 3 ⇄ 4 (cycle 2)\n");
let sccs = g_directed.strongly_connected_components();
let mut scc_groups: HashMap<usize, Vec<usize>> = HashMap::new();
for (node, &scc_id) in sccs.iter().enumerate().take(5) {
scc_groups.entry(scc_id).or_default().push(node);
}
println!(
" Found {} strongly connected components:",
scc_groups.len()
);
for (i, nodes) in scc_groups.values().enumerate() {
println!(" SCC {}: {:?}", i + 1, nodes);
}
println!();
println!("─────────────────────────────────────────────────────────────────");
println!("4️⃣ Topological Sort (DAG ordering)");
println!("─────────────────────────────────────────────────────────────────");
let dag_edges = vec![
(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), ];
let dag = Graph::from_edges(&dag_edges, true);
let tasks = [
"Setup Environment",
"Install Dependencies",
"Configure System",
"Build Project",
"Run Tests",
];
println!(" Task dependency graph (DAG):");
for (u, v) in &dag_edges {
println!(" {} → {}", tasks[*u], tasks[*v]);
}
println!();
match dag.topological_sort() {
Some(order) => {
println!(" Valid execution order:");
for (i, &task_id) in order.iter().enumerate() {
println!(" {}. {}", i + 1, tasks[task_id]);
}
}
None => {
println!(" ❌ Cycle detected! No valid ordering.");
}
}
println!();
}
fn demo_community_link_analysis() {
println!("\n╔═══════════════════════════════════════════════════════════════╗");
println!("║ PHASE 3: COMMUNITY & LINK ANALYSIS ║");
println!("╚═══════════════════════════════════════════════════════════════╝\n");
let social_edges = vec![
(0, 1),
(1, 2),
(2, 3),
(3, 0),
(0, 2), (3, 4),
(4, 5),
(5, 6),
(6, 7),
(7, 4),
(4, 6), ];
let g_social = Graph::from_edges(&social_edges, false);
println!("─────────────────────────────────────────────────────────────────");
println!("1️⃣ Label Propagation (community detection)");
println!("─────────────────────────────────────────────────────────────────");
println!(" Social network with 2 communities connected by a bridge:");
println!(" Community A: {{0,1,2,3}} ←bridge(3-4)→ Community B: {{4,5,6,7}}\n");
let communities = g_social.label_propagation(10, Some(42));
let mut comm_groups: std::collections::HashMap<usize, Vec<usize>> =
std::collections::HashMap::new();
for (node, &comm_id) in communities.iter().enumerate().take(8) {
comm_groups.entry(comm_id).or_default().push(node);
}
println!(" Detected {} communities:", comm_groups.len());
for (i, nodes) in comm_groups.values().enumerate() {
println!(" Community {}: {:?}", i + 1, nodes);
}
println!();
println!("─────────────────────────────────────────────────────────────────");
println!("2️⃣ Common Neighbors (link prediction)");
println!("─────────────────────────────────────────────────────────────────");
println!(" Link prediction: Will nodes 1 and 3 become friends?\n");
let cn_1_3 = g_social.common_neighbors(1, 3).expect("Nodes exist");
println!(" Common neighbors of 1 and 3: {cn_1_3}");
let neighbors_1: Vec<usize> = g_social.neighbors(1).to_vec();
let neighbors_3: Vec<usize> = g_social.neighbors(3).to_vec();
println!(" Node 1 neighbors: {neighbors_1:?}");
println!(" Node 3 neighbors: {neighbors_3:?}");
let common: Vec<usize> = neighbors_1
.iter()
.filter(|n| neighbors_3.contains(n))
.copied()
.collect();
println!(" Actual common neighbors: {common:?}");
println!(" → High common neighbor count suggests likely future connection\n");
println!("─────────────────────────────────────────────────────────────────");
println!("3️⃣ Adamic-Adar Index (weighted link prediction)");
println!("─────────────────────────────────────────────────────────────────");
let aa_1_3 = g_social.adamic_adar_index(1, 3).expect("Nodes exist");
println!(" Adamic-Adar score for nodes 1 and 3: {aa_1_3:.4}");
let aa_0_7 = g_social.adamic_adar_index(0, 7).expect("Nodes exist");
println!(" Adamic-Adar score for nodes 0 and 7: {aa_0_7:.4}");
println!("\n 💡 Interpretation:");
println!(" - Higher score = stronger prediction for future link");
println!(" - Nodes 1-3 (same community): {aa_1_3:.4}");
println!(" - Nodes 0-7 (different communities): {aa_0_7:.4}");
println!(" → Algorithm correctly identifies within-community links as more likely\n");
}