From 1d0154122b4109040da26185e170513a516c61c7 Mon Sep 17 00:00:00 2001 From: BoolPurist Date: Fri, 30 Aug 2024 06:00:23 +0200 Subject: [PATCH] Included showing puzzel input file into benchmark report Tidied up benchmakr report --- crates/cli/src/benchmarking.rs | 69 ++++++---- ...hmark_result.rs => conducted_benchmark.rs} | 17 ++- .../cli/src/benchmarking/file_to_benchmark.rs | 9 +- crates/cli/src/benchmarking/row_builder.rs | 124 ++++++++++++++++++ crates/cli/src/benchmarking/row_building.rs | 50 +++---- crates/cli/src/constants.rs | 1 + crates/cli/src/solving_given.rs | 29 ++-- crates/solutions/src/utils/parsing.rs | 4 +- 8 files changed, 225 insertions(+), 78 deletions(-) rename crates/cli/src/benchmarking/{benchmark_result.rs => conducted_benchmark.rs} (82%) create mode 100644 crates/cli/src/benchmarking/row_builder.rs diff --git a/crates/cli/src/benchmarking.rs b/crates/cli/src/benchmarking.rs index 49a4e49..9ffb343 100644 --- a/crates/cli/src/benchmarking.rs +++ b/crates/cli/src/benchmarking.rs @@ -1,43 +1,55 @@ -pub use benchmark_result::BenchmarkResult; +pub use conducted_benchmark::ConductedBenchmark; pub use file_to_benchmark::FileToBenchmark; -mod benchmark_result; +use row_builder::COLUMN_NUMBER; +use row_building::MatrixReport; + +mod conducted_benchmark; mod file_to_benchmark; +mod row_builder; mod row_building; -use crate::{cli::BenchmarkCli, solving_given::solve_given, AppResult}; +use crate::{ + cli::BenchmarkCli, + solving_given::{solve_given, NoSolutionFound}, + AppError, AppResult, +}; use anyhow::anyhow; use colonnade::Colonnade; use std::time::{Duration, Instant}; -fn calc_average(sum: Duration, nanos_count: u64) -> Duration { - let average_raw = sum.div_duration_f64(Duration::from_nanos(nanos_count)); - Duration::from_nanos(average_raw.round() as u64) -} +pub type BenchmarkResult = Result; + pub fn execute_benchmark(args: &BenchmarkCli) -> AppResult { let loaded = load_benchmarks(args)?; - let benchmarked = loaded - .into_iter() - .map(solve_and_keep_track_of_runtime) - .collect::>>()?; - let sum: Duration = benchmarked.iter().map(|result| result.how_long()).sum(); - let average = calc_average(sum, benchmarked.len() as u64); + + let benchmarked = loaded.into_iter().map(solve_and_keep_track_of_runtime); + + let mut sum: Duration = Duration::ZERO; + let mut count: u64 = 0; + let after_header = row_building::create_rows_for_every_solutions(benchmarked, |to_add| { + sum += to_add; + count += 1; + }); + let average = calc_average(sum, count); let header = row_building::create_header(); - let after_header = row_building::create_rows_for_every_solutions(benchmarked); - let table: Vec> = after_header + + let table: MatrixReport = after_header .into_iter() .chain(row_building::create_sum_row(sum)) .chain(row_building::create_average_row(average)) .chain(header) .collect(); - let lines = Colonnade::new(5, 100).unwrap().tabulate(table)?; + let lines = Colonnade::new(COLUMN_NUMBER, 100) + .unwrap() + .tabulate(table)?; Ok(lines.join("\n")) } fn solve_and_keep_track_of_runtime( (benchmark, content): (FileToBenchmark, String), -) -> AppResult { +) -> Result { let (day, task) = (benchmark.given_day(), benchmark.given_task()); let before = Instant::now(); let actual_ouput = solve_given(day, task, &content)?; @@ -45,15 +57,14 @@ fn solve_and_keep_track_of_runtime( let how_long = after - before; - let result = BenchmarkResult::new(benchmark, actual_ouput, how_long); + let result = ConductedBenchmark::new(benchmark, actual_ouput, how_long); Ok(result) } fn load_benchmarks(args: &BenchmarkCli) -> AppResult> { - let benchmarks = args - .files() + args.files() .iter() - .map(|path| { + .flat_map(|path| { let content = std::fs::read_to_string(path).map_err(|error| { anyhow!( "Could not read benchmark file at {:?}\n\ @@ -70,14 +81,9 @@ fn load_benchmarks(args: &BenchmarkCli) -> AppResult(parsed) }) - .collect::>>>()? - .into_iter() - .flatten(); - - benchmarks - .into_iter() + .flatten() .map(|benchmark| { let where_to_look = benchmark.where_to_look(); let content = std::fs::read_to_string(where_to_look).map_err(|error| { @@ -92,5 +98,10 @@ fn load_benchmarks(args: &BenchmarkCli) -> AppResult>>() + .collect() +} + +fn calc_average(sum: Duration, nanos_count: u64) -> Duration { + let average_raw = sum.div_duration_f64(Duration::from_nanos(nanos_count)); + Duration::from_nanos(average_raw.round() as u64) } diff --git a/crates/cli/src/benchmarking/benchmark_result.rs b/crates/cli/src/benchmarking/conducted_benchmark.rs similarity index 82% rename from crates/cli/src/benchmarking/benchmark_result.rs rename to crates/cli/src/benchmarking/conducted_benchmark.rs index 15d5d17..5aafeb1 100644 --- a/crates/cli/src/benchmarking/benchmark_result.rs +++ b/crates/cli/src/benchmarking/conducted_benchmark.rs @@ -1,21 +1,25 @@ -use std::time::Duration; +use std::{ + path::{Path, PathBuf}, + time::Duration, +}; use crate::cli::{GivenDay, GivenTask}; use super::FileToBenchmark; #[derive(Debug)] -pub struct BenchmarkResult { +pub struct ConductedBenchmark { task: GivenTask, day: GivenDay, how_long: Duration, maybe_different_actual_output: Option, expected_output: String, + path_of_input: PathBuf, } -impl BenchmarkResult { +impl ConductedBenchmark { pub fn new(benchmarking: FileToBenchmark, actual_ouput: String, how_long: Duration) -> Self { - let (day, task, expected_output) = benchmarking.into(); + let (day, task, expected_output, path_of_input) = benchmarking.into(); let maybe_different_actual_output = if actual_ouput == expected_output { None } else { @@ -27,6 +31,7 @@ impl BenchmarkResult { how_long, maybe_different_actual_output, expected_output, + path_of_input, } } @@ -63,4 +68,8 @@ impl BenchmarkResult { let mili_secs = how_long.as_nanos() % 1_000_000_000; format!("{}:{}", secs, mili_secs) } + + pub fn path_of_input(&self) -> &Path { + &self.path_of_input + } } diff --git a/crates/cli/src/benchmarking/file_to_benchmark.rs b/crates/cli/src/benchmarking/file_to_benchmark.rs index c365093..4107d05 100644 --- a/crates/cli/src/benchmarking/file_to_benchmark.rs +++ b/crates/cli/src/benchmarking/file_to_benchmark.rs @@ -19,9 +19,14 @@ pub struct FileToBenchmark { expected_output: String, } -impl From for (GivenDay, GivenTask, String) { +impl From for (GivenDay, GivenTask, String, PathBuf) { fn from(val: FileToBenchmark) -> Self { - (val.given_day, val.given_task, val.expected_output) + ( + val.given_day, + val.given_task, + val.expected_output, + val.where_to_look, + ) } } diff --git a/crates/cli/src/benchmarking/row_builder.rs b/crates/cli/src/benchmarking/row_builder.rs new file mode 100644 index 0000000..2e4d36a --- /dev/null +++ b/crates/cli/src/benchmarking/row_builder.rs @@ -0,0 +1,124 @@ +use std::{path::PathBuf, time::Duration}; + +const AVERAGE: &str = "Average"; +const TOTAL: &str = "total"; + +use crate::{ + benchmarking::ConductedBenchmark, + cli::{GivenDay, GivenTask}, + constants, + solving_given::NoSolutionFound, +}; + +pub const COLUMN_NUMBER: usize = 6; + +#[derive(Debug, Default)] +pub struct RowBuilder { + day: Option, + task: Option, + taken_time: Option, + actual_result: Option, + expected_result: Option, + path_to_input: Option, +} + +impl From for RowBuilder { + fn from(value: NoSolutionFound) -> Self { + match value { + NoSolutionFound::DayNotFound(day) => Self::no_day_found(day), + NoSolutionFound::TaskNotFound { day, task } => Self::no_task_found(day, task), + } + } +} + +impl From for RowBuilder { + fn from(value: ConductedBenchmark) -> Self { + Self::new( + value.day(), + value.task(), + value.how_long(), + value + .maybe_different_actual_output() + .map(ToString::to_string) + .unwrap_or_default(), + value.expected_output().to_string(), + value.path_of_input().to_path_buf(), + ) + } +} + +impl RowBuilder { + pub fn new( + day: GivenDay, + task: GivenTask, + taken_time: Duration, + actual_result: String, + expected_result: String, + path: PathBuf, + ) -> Self { + Self { + day: Some(day.to_string()), + task: Some(task.to_string()), + taken_time: Some(taken_time), + actual_result: Some(actual_result), + expected_result: Some(expected_result), + path_to_input: Some(path.to_string_lossy().to_string()), + } + } + + pub fn no_day_found(wrong_day: GivenDay) -> Self { + let day = Some(format!("Solution found for day {}", wrong_day)); + Self { + day, + ..Default::default() + } + } + + pub fn no_task_found(right_day: GivenDay, wrong_task: GivenTask) -> Self { + let day = Some(right_day.to_string()); + let task = Some(format!("No solution found for task {}", wrong_task)); + Self { + day, + task, + ..Default::default() + } + } + + pub fn average(average: Duration) -> Self { + Self::new_aggreate(AVERAGE, average) + } + + pub fn total(total: Duration) -> Self { + Self::new_aggreate(TOTAL, total) + } + + pub fn into_row(self) -> Vec { + fn this_or_placeholder(value: Option) -> String { + value.unwrap_or_else(|| constants::PLACEHOLDER_IN_BENCHMARK_REPORTS.to_string()) + } + + let (day, task, taken_time, actual_result, expected_result, path) = ( + this_or_placeholder(self.day), + this_or_placeholder(self.task), + this_or_placeholder( + self.taken_time + .map(ConductedBenchmark::convert_duration_to_secs_and_mili_txt), + ), + this_or_placeholder(self.actual_result), + this_or_placeholder(self.expected_result), + this_or_placeholder(self.path_to_input), + ); + vec![day, task, taken_time, actual_result, expected_result, path] + } + + fn new_aggreate(label: &str, aggregate: Duration) -> Self { + let (day, task) = (Some(label.to_string()), Some(label.to_string())); + let taken_time = Some(aggregate); + Self { + day, + task, + taken_time, + ..Default::default() + } + } +} diff --git a/crates/cli/src/benchmarking/row_building.rs b/crates/cli/src/benchmarking/row_building.rs index d068981..3061d1a 100644 --- a/crates/cli/src/benchmarking/row_building.rs +++ b/crates/cli/src/benchmarking/row_building.rs @@ -1,7 +1,8 @@ -use std::time::Duration; +pub type MatrixReport = Vec>; +pub type SingleRow = std::iter::Once>; -use super::BenchmarkResult; -type MatrixReport = Vec>; +use super::{row_builder::RowBuilder, BenchmarkResult}; +use std::time::Duration; pub fn create_header() -> std::iter::Once> { std::iter::once(vec![ @@ -10,45 +11,30 @@ pub fn create_header() -> std::iter::Once> { "How long (Seconds:Mili)".to_string(), "Expected".to_string(), "Actual".to_string(), + "Used input file".to_string(), ]) } -pub fn create_sum_row(sum: Duration) -> std::iter::Once> { - let passed_txt = BenchmarkResult::convert_duration_to_secs_and_mili_txt(sum); - std::iter::once(vec![ - "Total".to_string(), - "Total".to_string(), - passed_txt, - "-".to_string(), - "-".to_string(), - ]) +pub fn create_sum_row(sum: Duration) -> SingleRow { + std::iter::once(RowBuilder::total(sum).into_row()) } -pub fn create_average_row(average: Duration) -> std::iter::Once> { - let passed_txt = BenchmarkResult::convert_duration_to_secs_and_mili_txt(average); - std::iter::once(vec![ - "Average".to_string(), - "Average".to_string(), - passed_txt, - "-".to_string(), - "-".to_string(), - ]) +pub fn create_average_row(average: Duration) -> SingleRow { + std::iter::once(RowBuilder::average(average).into_row()) } -pub fn create_rows_for_every_solutions(loaded: Vec) -> MatrixReport { +pub fn create_rows_for_every_solutions( + loaded: impl IntoIterator, + mut on_ok: impl FnMut(Duration), +) -> MatrixReport { loaded .into_iter() .map(|result| { - vec![ - result.day().to_string(), - result.task().to_string(), - result.how_long_as_string(), - result.expected_output().to_string(), - result - .maybe_different_actual_output() - .map(|unowned| unowned.to_string()) - .unwrap_or_else(|| result.expected_output().to_string()), - ] + result + .inspect(|on_success| on_ok(on_success.how_long())) + .map(RowBuilder::from) + .unwrap_or_else(RowBuilder::from) }) + .map(RowBuilder::into_row) .collect() } diff --git a/crates/cli/src/constants.rs b/crates/cli/src/constants.rs index 7064e4d..51a5e5b 100644 --- a/crates/cli/src/constants.rs +++ b/crates/cli/src/constants.rs @@ -1,2 +1,3 @@ pub const MAX_DAY: u32 = 25; pub const MAX_TASK: u32 = 2; +pub const PLACEHOLDER_IN_BENCHMARK_REPORTS: &str = "-"; diff --git a/crates/cli/src/solving_given.rs b/crates/cli/src/solving_given.rs index 24f7a28..5f454ca 100644 --- a/crates/cli/src/solving_given.rs +++ b/crates/cli/src/solving_given.rs @@ -7,16 +7,23 @@ use crate::{ AppResult, }; -pub fn solve_given(given_day: GivenDay, given_task: GivenTask, content: &str) -> AppResult { +pub fn solve_given( + given_day: GivenDay, + given_task: GivenTask, + content: &str, +) -> Result { let found_task = { let day: u32 = given_day.into(); let task: u32 = given_task.into(); let found_day = ALL_SOLUTIONS .get(day.saturating_sub(1) as usize) - .ok_or_else(|| CouldNotSolveError::DayNotFound(day))?; + .ok_or(NoSolutionFound::DayNotFound(given_day))?; found_day .get(task.saturating_sub(1) as usize) - .ok_or_else(|| CouldNotSolveError::TaskNotFound { day, task }) + .ok_or(NoSolutionFound::TaskNotFound { + day: given_day, + task: given_task, + }) }?; let solved = (found_task)(content); @@ -40,11 +47,17 @@ fn try_read_from_file_if_demanded(args: &CliSolutionToSolve) -> io::Result( None }) } -pub fn blocks_of_lines_seperated_by_empty_lines<'a>( - input: &'a str, -) -> impl Iterator> { +pub fn blocks_of_lines_seperated_by_empty_lines(input: &str) -> impl Iterator> { blocks_of_lines_seperated_by(input, |line| line.trim().is_empty()) }