use super::algorithm::{
BFGTStateGraph, JumpStateGraph, LogStateGraph, NaiveStateGraph,
SimpleStateGraph,
};
use super::constants::EXAMPLE_IN_EXT;
use super::example::{Example, ExampleOutput, ExampleResult};
use super::interface::StateGraph;
use super::util;
use std::fmt::{self, Debug};
use std::ops::DerefMut;
use std::str::FromStr;
use std::time::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Algorithm {
Naive,
Simple,
BFGT,
Log,
Jump,
}
impl FromStr for Algorithm {
type Err = String;
fn from_str(s: &str) -> Result<Self, String> {
match s.to_lowercase().as_str() {
"n" | "naive" => Ok(Algorithm::Naive),
"s" | "simple" => Ok(Algorithm::Simple),
"b" | "bfgt" => Ok(Algorithm::BFGT),
"l" | "log" => Ok(Algorithm::Log),
"j" | "jump" => Ok(Algorithm::Jump),
_ => Err(format!("Could not parse as Algorithm: {}", s)),
}
}
}
impl fmt::Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let result = match self {
Algorithm::Naive => "naive",
Algorithm::Simple => "simple",
Algorithm::BFGT => "bfgt",
Algorithm::Log => "log",
Algorithm::Jump => "jump",
};
write!(f, "{}", result)
}
}
impl Algorithm {
fn new_graph(&self) -> Box<dyn StateGraph> {
match self {
Algorithm::Naive => Box::new(NaiveStateGraph::new()),
Algorithm::Simple => Box::new(SimpleStateGraph::new()),
Algorithm::BFGT => Box::new(BFGTStateGraph::new()),
Algorithm::Log => Box::new(LogStateGraph::new()),
Algorithm::Jump => Box::new(JumpStateGraph::new()),
}
}
}
fn run_core(
example: &Example,
alg: Algorithm,
timeout: Duration,
verbose: bool,
) -> ExampleResult {
if verbose {
println!("===== {} =====", example.name());
println!(
"Running algorithm '{}' with timeout {}s...",
alg,
timeout.as_secs()
);
}
let mut graph = alg.new_graph();
let result = example.run_with_timeout(graph.deref_mut(), timeout);
if verbose {
println!("=== Output ===");
println!("{}", result.output_str());
println!("=== Result ===");
println!("Stastics: {}", result.summary());
if example.expected.is_some() {
if result.is_correct() {
println!("Output is correct.");
} else {
println!("Output is incorrect!");
println!("=== Expected Output ===");
println!("{:?}", example.expected.as_ref().unwrap());
}
}
} else {
println!("{}: {}", alg, result.summary());
}
result
}
pub fn run_single_example(
basename: &str,
algorithm: Algorithm,
timeout_secs: u64,
) {
let example = Example::load_from(basename);
let timeout = Duration::from_secs(timeout_secs);
run_core(&example, algorithm, timeout, true);
}
pub const ALL_ALGS: &[Algorithm] = &[
Algorithm::Naive,
Algorithm::Simple,
Algorithm::BFGT,
Algorithm::Log,
Algorithm::Jump,
];
pub fn algs_excluding(exclude: &[Algorithm]) -> Vec<Algorithm> {
ALL_ALGS.iter().filter(|&x| !exclude.contains(x)).cloned().collect()
}
pub fn algs_all() -> Vec<Algorithm> {
algs_excluding(&[])
}
pub fn unwrap_timeout(res: &ExampleResult) -> &ExampleOutput {
res.get_output().unwrap_or_else(|| {
panic!(
"Algorithm timed out! Tip: if you are running \
--ignored tests, make sure to use --release mode."
);
})
}
pub fn assert_example(basename: &str, timeout_secs: Option<u64>) {
let example = Example::load_from(basename);
let timeout_secs = timeout_secs.unwrap_or(u64::MAX);
let timeout = Duration::from_secs(timeout_secs);
let algs = algs_all();
if example.expected.is_some() {
println!("Asserting each algorithm output matches expected...");
for alg in algs {
let out = run_core(&example, alg, timeout, true);
assert!(out.is_correct());
}
} else {
assert!(!algs.is_empty());
println!("Asserting each algorithm output matches {}...", algs[0]);
let out = run_core(&example, algs[0], timeout, true);
let expected = unwrap_timeout(&out);
for &alg in algs.iter().skip(1) {
let out = run_core(&example, alg, timeout, true);
assert_eq!(expected, unwrap_timeout(&out));
}
}
}
pub fn run_compare_csv_header(algs: &[Algorithm]) -> String {
let mut header = "name, size, timeout".to_string();
for alg in algs {
header += &format!(", time ({})", alg);
if cfg!(debug_assertions) {
header += &format!(", space ({})", alg);
}
}
header
}
pub fn run_compare(
basename: &str,
algs: &[Algorithm],
timeout_secs: u64,
) -> String {
let example = Example::load_from(basename);
println!("===== {} =====", example.name());
println!("Example size: {}, timeout: {}s", example.len(), timeout_secs);
let mut result =
format!("{}, {}, {}", example.name(), example.len(), timeout_secs);
let timeout = Duration::from_secs(timeout_secs);
for &alg in algs {
let out = run_core(&example, alg, timeout, false);
result += &format!(", {}", out.time_str());
if cfg!(debug_assertions) {
result += &format!(", {}", out.space_str());
}
}
result
}
pub fn example_basenames_in_dir(dir: &str) -> Vec<String> {
util::paths_in(dir)
.filter_map(|path| path.strip_suffix(EXAMPLE_IN_EXT).map(String::from))
.collect()
}