use trueno_graph::{bfs, find_callers, pagerank, CsrGraph, NodeId};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🦀 trueno-graph Phase 2: Graph Algorithms\n");
println!("📊 Building call graph...");
let mut graph = CsrGraph::new();
graph.add_edge(NodeId(0), NodeId(1), 1.0)?; graph.add_edge(NodeId(0), NodeId(2), 1.0)?; graph.add_edge(NodeId(0), NodeId(3), 1.0)?;
graph.add_edge(NodeId(1), NodeId(2), 1.0)?; graph.add_edge(NodeId(1), NodeId(4), 1.0)?;
graph.add_edge(NodeId(3), NodeId(5), 1.0)?; graph.add_edge(NodeId(3), NodeId(6), 1.0)?;
graph.add_edge(NodeId(5), NodeId(7), 1.0)?; graph.add_edge(NodeId(5), NodeId(8), 1.0)?;
graph.add_edge(NodeId(8), NodeId(5), 0.5)?;
let names = vec![
"main",
"parse_args",
"validate_config",
"execute",
"read_file",
"process_data",
"write_output",
"analyze",
"transform",
];
for (id, name) in names.iter().enumerate() {
graph.set_node_name(NodeId(id as u32), (*name).to_string());
}
println!(
" ✅ Graph built: {} nodes, {} edges\n",
graph.num_nodes(),
graph.num_edges()
);
println!("🔍 BFS: Finding all functions reachable from main()...");
let reachable = bfs(&graph, NodeId(0))?;
println!(" Found {} reachable functions:", reachable.len());
for &node in &reachable {
let name = graph.get_node_name(NodeId(node)).unwrap_or("unknown");
println!(" → {}", name);
}
println!("\n🔍 Finding all callers of validate_config()...");
let callers = find_callers(&graph, NodeId(2), 10)?;
println!(" Found {} callers:", callers.len());
for &caller in &callers {
let name = graph.get_node_name(NodeId(caller)).unwrap_or("unknown");
println!(" ← {}", name);
}
println!("\n📊 Computing PageRank importance scores...");
let scores = pagerank(&graph, 20, 1e-6)?;
let mut ranked: Vec<_> = (0..graph.num_nodes())
.map(|i| {
let name = graph.get_node_name(NodeId(i as u32)).unwrap_or("unknown");
(name, scores[i])
})
.collect();
ranked.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
println!(" Top 5 most important functions:");
for (i, (name, score)) in ranked.iter().take(5).enumerate() {
println!(" {}. {} (score: {:.4})", i + 1, name, score);
}
println!("\n🔍 Finding all callers of process_data() (depth 5)...");
let process_callers = find_callers(&graph, NodeId(5), 5)?;
println!(" Found {} functions in call chain:", process_callers.len());
for &caller in &process_callers {
let name = graph.get_node_name(NodeId(caller)).unwrap_or("unknown");
let score = scores[caller as usize];
println!(" ← {} (importance: {:.4})", name, score);
}
println!("\n💾 Saving to Parquet...");
let path = std::env::temp_dir().join("call_graph");
graph.write_parquet(&path).await?;
println!(" ✅ Saved to {}_edges.parquet", path.display());
println!(" ✅ Saved to {}_nodes.parquet", path.display());
println!("\n📂 Loading from Parquet...");
let loaded = CsrGraph::read_parquet(&path).await?;
let loaded_scores = pagerank(&loaded, 20, 1e-6)?;
let diff: f32 = scores
.iter()
.zip(loaded_scores.iter())
.map(|(a, b)| (a - b).abs())
.sum();
println!(
" ✅ Loaded: {} nodes, {} edges",
loaded.num_nodes(),
loaded.num_edges()
);
println!(
" ✅ PageRank consistency: {:.2e} (sum of absolute differences)",
diff
);
println!("\n✨ Phase 2 algorithms complete!");
println!("\nSummary:");
println!(
" • BFS found {} reachable functions from main()",
reachable.len()
);
println!(" • validate_config() has {} callers", callers.len());
println!(
" • Most important: {} (score: {:.4})",
ranked[0].0, ranked[0].1
);
println!(" • Parquet roundtrip verified (diff: {:.2e})", diff);
Ok(())
}