extern crate core; use std::time::Instant; use crate::day10::Day10; use crate::day11::Day11; use crate::day12::Day12; use crate::day1::Day1; use crate::day2::Day2; use crate::day3::Day3; use crate::day4::Day4; use crate::day5::Day5; use crate::day6::Day6; use crate::day7::Day7; use crate::day8::Day8; use crate::day9::Day9; use crate::day_solver::DaySolver; use crate::util::read_file; mod util; mod day_solver; mod day1; mod day2; mod day3; mod day4; mod day5; mod day6; mod day7; mod day8; mod day9; mod day10; mod day11; mod day12; const DEFAULT_BENCHMARK_AMOUNT: u32 = 100; fn main() { let args: Vec = std::env::args().collect(); let day_arg_idx = args.iter().position(|a| a == "-d"); let single_day = day_arg_idx.is_some(); let day = if single_day { args[day_arg_idx.unwrap() + 1].parse::().unwrap() } else { 0 }; let benchmark_arg_idx_option = args.iter().position(|a| a == "--bench").or( args.iter().position(|a| a == "-b")); let benchmark = benchmark_arg_idx_option.is_some(); let bench_amount: u32 = if let Some(benchmark_arg_idx) = benchmark_arg_idx_option { args.get(benchmark_arg_idx + 1) .map_or(DEFAULT_BENCHMARK_AMOUNT, |r| r.parse::().unwrap_or(DEFAULT_BENCHMARK_AMOUNT)) } else { DEFAULT_BENCHMARK_AMOUNT }; let mut bench_results: Vec = Vec::new(); // This is essentially the warmup for the benchmark: run_once(single_day, day, false, &mut bench_results); let first_run_bench = bench_results[0].to_owned(); let (_, total_time) = bench(|| if benchmark { // Ignore the warmup run in the rest of the benchmark: bench_results.clear(); for _ in 0..bench_amount { run_once(single_day, day, true, &mut bench_results); } }); if benchmark { println!("Executed {} rounds in {:.3} s ({} μs, or {} μs on average per round)", bench_results.len(), total_time as f64 / 1000000f64, total_time, total_time / bench_amount as u128); print_bench_result(&bench_results, |b| b.total, "Execution"); print_bench_result(&bench_results, |b| b.read_file, "Reading file"); print_bench_result(&bench_results, |b| b.init, "Initialization"); print_bench_result(&bench_results, |b| b.part1, "Part 1"); print_bench_result(&bench_results, |b| b.part2, "Part 2"); print_bench_result(&bench_results, |b| b.part1 + b.part2, "Part 1 & 2 together"); println!("First time took {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.read_file, first_run_bench.init, first_run_bench.part1, first_run_bench.part2); println!("Average: {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)", avg_bench(&bench_results, |b| b.total), avg_bench(&bench_results, |b| b.read_file), avg_bench(&bench_results, |b| b.init), avg_bench(&bench_results, |b| b.part1), avg_bench(&bench_results, |b| b.part2)); } else { println!("Execution took {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.read_file, first_run_bench.init, first_run_bench.part1, first_run_bench.part2); } } #[derive(Copy, Clone)] struct AocBenchResult { read_file: u128, init: u128, part1: u128, part2: u128, total: u128 } fn build_day_solver(day: u8, input: String) -> Option> { match day { 1 => Some(Box::new(Day1::create(input))), 2 => Some(Box::new(Day2::create(input))), 3 => Some(Box::new(Day3::create(input))), 4 => Some(Box::new(Day4::create(input))), 5 => Some(Box::new(Day5::create(input))), 6 => Some(Box::new(Day6::create(input))), 7 => Some(Box::new(Day7::create(input))), 8 => Some(Box::new(Day8::create(input))), 9 => Some(Box::new(Day9::create(input))), 10 => Some(Box::new(Day10::create(input))), 11 => Some(Box::new(Day11::create(input))), 12 => Some(Box::new(Day12::create(input))), _ => None } } fn solve(day: u8, silent: bool) -> AocBenchResult { let now = Instant::now(); let (input, read_file_time) = bench(|| { let input_filename = format!("input/day{:02}.txt",day); read_file(&input_filename) }); let (solver, init_time) = bench(|| build_day_solver(day, input.to_owned())); let part1_time: u128; let part2_time: u128; match solver { Some(mut s) => { let(part1, pt1_time) = bench(|| s.solve_part1()); part1_time = pt1_time; if !silent { println!("Day {} Part 1: {}", day, part1); } let (part2, pt2_time) = bench(|| s.solve_part2()); part2_time = pt2_time; if !silent { println!("Day {} Part 2: {}", day, part2); } }, None => panic!("This day is not yet implemented") } return AocBenchResult{ read_file: read_file_time, init: init_time, part1: part1_time, part2: part2_time, total: now.elapsed().as_micros() } } fn print_bench_result(bench_results: &Vec, f: F, bench_part_description: &str) where F: FnMut(&AocBenchResult) -> u128 { let mut benches: Vec = bench_results.iter().map(f).collect(); benches.sort(); let avg_runtime: u128 = benches.iter().sum::() / (bench_results.len() as u128); println!("{} took {} μs {} (Min: {} μs, Max: {} μs, Median: {} μs)", bench_part_description, avg_runtime, if bench_results.len() > 1 { "on average"} else {""}, benches[0], benches[benches.len() - 1], benches[benches.len() / 2]) } fn avg_bench(bench_results: &Vec, f: F) -> u128 where F: FnMut(&AocBenchResult) -> u128 { let benches: Vec = bench_results.iter().map(f).collect(); return benches.iter().sum::() / (bench_results.len() as u128) } fn run_once(single_day: bool, day: u8, silent: bool, bench_results: &mut Vec) { let bench_result = if single_day { solve(day, silent) } else { solve_all(silent) }; bench_results.push(bench_result); } fn solve_all(silent: bool) -> AocBenchResult { let mut bench_results = Vec::new(); let max_day = (1..).take_while(|d| build_day_solver(*d, "".to_string()).is_some()).last().unwrap(); for day in 1..(max_day + 1) { bench_results.push(solve(day, silent)); } return AocBenchResult { read_file: bench_results.iter().map(|t| t.read_file).sum(), init: bench_results.iter().map(|t| t.init).sum(), part1: bench_results.iter().map(|t| t.part1).sum(), part2: bench_results.iter().map(|t| t.part2).sum(), total: bench_results.iter().map(|t| t.total).sum(), } } fn bench(mut f: F) -> (K, u128) where F: FnMut() -> K { let now = Instant::now(); let res = f(); (res, now.elapsed().as_micros()) }