use super::constants::{EXAMPLE_EXPECT_EXT, EXAMPLE_IN_EXT};
use super::interface::{StateGraph, Status, Transaction};
use super::util;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::path::PathBuf;
use std::time::{Duration, SystemTime};
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct ExampleInput(pub Vec<Transaction>);
impl ExampleInput {
pub fn new() -> Self {
Default::default()
}
pub fn push(&mut self, t: Transaction) {
self.0.push(t);
}
pub fn get_states(&self) -> HashSet<usize> {
let mut result = HashSet::new();
for &t in &self.0 {
match t {
Transaction::Add(v1, v2) => {
result.insert(v1);
result.insert(v2);
}
Transaction::Close(v1) => {
result.insert(v1);
}
Transaction::Live(v1) => {
result.insert(v1);
}
}
}
result
}
}
#[derive(Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct ExampleOutput {
pub live: Vec<usize>,
pub dead: Vec<usize>,
pub unknown: Vec<usize>,
pub open: Vec<usize>,
}
impl ExampleOutput {
pub fn new() -> Self {
Default::default()
}
pub fn add(&mut self, v: usize, st: Status) {
match st {
Status::Live => self.live.push(v),
Status::Dead => self.dead.push(v),
Status::Unknown => self.unknown.push(v),
Status::Open => self.open.push(v),
};
}
pub fn finalize(&mut self) {
self.live.sort_unstable();
self.dead.sort_unstable();
self.unknown.sort_unstable();
self.open.sort_unstable();
}
}
pub struct DebugStats {
output: ExampleOutput,
correct: bool,
time: usize,
space: usize,
}
pub struct ReleaseStats {
output: ExampleOutput,
correct: bool,
time: Duration,
}
pub enum ExampleResult {
Timeout,
Debug(DebugStats),
Release(ReleaseStats),
}
impl ExampleResult {
pub fn is_correct(&self) -> bool {
match self {
Self::Timeout => false,
Self::Debug(res) => res.correct,
Self::Release(res) => res.correct,
}
}
pub fn summary(&self) -> String {
if let Self::Timeout = self {
"Timeout".to_string()
} else if !self.is_correct() {
"Wrong Output".to_string()
} else if let Self::Debug(res) = self {
format!("time {}, space {}", res.time, res.space)
} else if let Self::Release(res) = self {
format!("time {}ms", res.time.as_millis())
} else {
unreachable!()
}
}
pub fn time_str(&self) -> String {
match self {
Self::Timeout => "Timeout".to_string(),
Self::Debug(res) => format!("{}", res.time),
Self::Release(res) => format!("{}", res.time.as_millis()),
}
}
pub fn space_str(&self) -> String {
match self {
Self::Timeout => "Timeout".to_string(),
Self::Debug(res) => format!("{}", res.space),
Self::Release(_) => "Unknown (not tracked)".to_string(),
}
}
pub fn get_output(&self) -> Option<&ExampleOutput> {
match self {
Self::Timeout => None,
Self::Debug(res) => Some(&res.output),
Self::Release(res) => Some(&res.output),
}
}
pub fn output_str(&self) -> String {
match self {
Self::Timeout => "Timeout".to_string(),
Self::Debug(res) => format!("{:?}", res.output),
Self::Release(res) => format!("{:?}", res.output),
}
}
}
fn infile_path(basename: &str) -> PathBuf {
PathBuf::from(format!("{}{}", basename, EXAMPLE_IN_EXT))
}
fn expectfile_path(basename: &str) -> PathBuf {
PathBuf::from(format!("{}{}", basename, EXAMPLE_EXPECT_EXT))
}
pub struct Example {
pub basename: String, pub input: ExampleInput,
pub expected: Option<ExampleOutput>,
}
impl Example {
pub fn new(
basename: &str,
input: ExampleInput,
expected: Option<ExampleOutput>,
) -> Self {
let basename = basename.to_string();
Self { basename, input, expected }
}
pub fn name(&self) -> &str {
&self.basename
}
pub fn len(&self) -> usize {
self.input.0.len()
}
pub fn is_empty(&self) -> bool {
self.input.0.is_empty()
}
pub fn load_from(basename: &str) -> Self {
let infile = infile_path(basename);
let expectfile = expectfile_path(basename);
let input = util::from_json_file(&infile);
let expected = if util::file_exists(&expectfile) {
util::from_json_file(&expectfile)
} else {
None
};
Self::new(basename, input, expected)
}
pub fn save(&self) {
util::to_json_file(infile_path(&self.basename), &self.input);
if let Some(expect) = &self.expected {
util::to_json_file(expectfile_path(&self.basename), &expect);
}
}
pub fn run_with_timeout<G: StateGraph + ?Sized>(
&self,
graph: &mut G,
timeout: Duration,
) -> ExampleResult {
let start = SystemTime::now();
for &t in &self.input.0 {
let time_elapsed = util::time_since(&start);
if time_elapsed > timeout {
return ExampleResult::Timeout;
}
graph.process(t);
}
let total_elapsed = util::time_since(&start);
let (output, correct) = self.collect_output(graph);
if cfg!(debug_assertions) {
let time = graph.get_time();
let space = graph.get_space();
ExampleResult::Debug(DebugStats { output, correct, time, space })
} else {
ExampleResult::Release(ReleaseStats {
output,
correct,
time: total_elapsed,
})
}
}
fn collect_output<G: StateGraph + ?Sized>(
&self,
graph: &mut G,
) -> (ExampleOutput, bool) {
let mut output = ExampleOutput::new();
for &v in self.input.get_states().iter() {
output.add(v, graph.get_status(v).unwrap_or(Status::Open));
}
output.finalize();
if let Some(expect) = &self.expected {
let correct = &output == expect;
(output, correct)
} else {
(output, true)
}
}
}