use std::{ fs::File, io::{BufRead, BufReader}, }; use clap::{App, Arg}; use pathfinding::prelude::kruskal; use pheap::graph::{mst_prim, SimpleGraph}; macro_rules! run_exp { ($runs:expr, $exe:stmt) => { let mut durations = Vec::with_capacity($runs); for ii in 0..$runs { println!("> Run {}/{}", ii + 1, $runs); let start = std::time::Instant::now(); $exe let end = std::time::Instant::now() - start; println!( "> Time taken to solve the problem: {} (ms)", end.as_millis() ); durations.push(end.as_millis()); } let avg = durations.iter().sum::() as usize; println!("Average time: {} (ms)", avg / $runs); }; } fn main() { let matches = App::new("Single source shortest path benchmark") .arg( Arg::with_name("file") .short("f") .long("file") .takes_value(true) .required(true) .help("Path to a DIMACS file."), ) .arg( Arg::with_name("lib") .long("lib") .takes_value(true) .required(true) .help("The library to be used to solve the shortest path problem. Options: pheap | fast_paths."), ) .arg( Arg::with_name("runs") .long("runs") .takes_value(true) .default_value("5") .help("Number of runs for search query."), ) .get_matches(); let filepath = match matches.value_of("file") { Some(fp) => fp, None => std::process::exit(1), }; let runs = matches .value_of("runs") .unwrap() .to_string() .parse::() .unwrap(); match matches.value_of("lib") { Some(lib) => match lib { "pheap" => graph(filepath, runs), "pathfinding" => pathfinding(filepath, runs), _ => std::process::exit(1), }, None => std::process::exit(1), }; } fn graph(filepath: &str, runs: usize) { println!("> Load file: {}", filepath); let file = File::open(filepath).unwrap(); let mut reader = BufReader::new(file); let mut n_nodes = 0; let mut _n_edges = 0; for _ in 0..7 { let mut line = String::new(); reader.read_line(&mut line).unwrap(); if !line.is_empty() && line.starts_with('p') { let s = line.trim().split_whitespace().collect::>(); n_nodes = s[2].parse::().unwrap(); _n_edges = s[3].parse::().unwrap(); } } let mut g = SimpleGraph::::with_capacity(n_nodes); for line in reader.lines() { let (node1, node2, weight) = parse_line(&line.unwrap()); g.add_weighted_edges(node1, node2, weight); } println!("> Graph created."); run_exp!(runs, let _ = mst_prim(&g, 0)); } fn pathfinding(filepath: &str, runs: usize) { println!("> Load file: {}", filepath); let file = File::open(filepath).unwrap(); let mut reader = BufReader::new(file); let mut _n_nodes = 0; let mut n_edges = 0; for _ in 0..7 { let mut line = String::new(); reader.read_line(&mut line).unwrap(); if !line.is_empty() && line.starts_with('p') { let s = line.trim().split_whitespace().collect::>(); _n_nodes = s[2].parse::().unwrap(); n_edges = s[3].parse::().unwrap(); } } let mut edges = Vec::with_capacity(n_edges); for line in reader.lines() { let (node1, node2, weight) = parse_line(&line.unwrap()); edges.push((node1, node2, weight)); edges.push((node2, node1, weight)); } run_exp!(runs, let _ = kruskal(&edges)); } fn parse_line(line: &str) -> (usize, usize, u32) { let s = line.trim().split_whitespace().collect::>(); let node1 = s[1].parse::().unwrap() - 1; let node2 = s[2].parse::().unwrap() - 1; let weight = s[3].parse::().unwrap(); (node1, node2, weight) }