From a8ece7c6d8b2fe1774a28bbc1b6b177915185e03 Mon Sep 17 00:00:00 2001 From: Ty Date: Sat, 16 Dec 2023 00:17:07 -0700 Subject: [PATCH] More benchmarking overcomplicated stuff Truly putting that AP Stats knowledge to the test --- Cargo.lock | 7 ++ cli/Cargo.toml | 3 +- cli/src/main.rs | 49 +++++----- cli/src/utils.rs | 11 +++ lib/src/day16/mod.rs | 210 ++++++++++++++++++++++++++++++++----------- 5 files changed, 206 insertions(+), 74 deletions(-) create mode 100644 cli/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 8c9b048..9f9e7b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,7 @@ version = "0.1.0" dependencies = [ "advent", "clap", + "indoc", "tokio", ] @@ -621,6 +622,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + [[package]] name = "ipnet" version = "2.9.0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 5848904..d808206 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] clap = { version = "4.4.10", features = ["derive"] } tokio = { version = "1.34.0", features = ["macros", "rt-multi-thread"] } -advent = { path = "../lib" } \ No newline at end of file +advent = { path = "../lib" } +indoc = "2.0.4" diff --git a/cli/src/main.rs b/cli/src/main.rs index c8df5e9..1c501b9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,3 +1,5 @@ +mod utils; + use std::{ io::Write, time::{Duration, Instant}, @@ -5,6 +7,7 @@ use std::{ use advent::fetcher; use clap::{Parser, Subcommand, ValueEnum}; +use indoc::printdoc; #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -152,32 +155,32 @@ async fn main() { } timings.drain(..=(warmup as usize)); let avg = (timings.iter().sum::() / n).as_nanos(); - let deviation = (((timings.iter().max().unwrap().as_nanos() - - timings.iter().min().unwrap().as_nanos()) - .pow(2) - / 16) as f64) - .sqrt() as u128; - - let (avg, avg_units) = if avg >= 1000000000 { - (avg as f64 / 1000000000.0, "s") - } else if avg >= 1000000 { - (avg as f64 / 1000000.0, "ms") - } else if avg >= 1000 { - (avg as f64 / 1000.0, "µs") + let max = timings.iter().max().unwrap().as_nanos(); + let min = timings.iter().min().unwrap().as_nanos(); + let deviation = (((max - min).pow(2) / 16) as f64).sqrt() as u128; + let range = max - min; + let median = if timings.len() % 2 == 0 { + (timings[timings.len()/2-1]+timings[timings.len()/2]).as_nanos() as f64 / 2.0 } else { - (avg as f64, "ns") + timings[timings.len()/2].as_nanos() as f64 }; + + let (avg, avg_units) = utils::unitify(avg as f64); + let (max, max_units) = utils::unitify(max as f64); + let (min, min_units) = utils::unitify(min as f64); + let (deviation, deviation_units) = utils::unitify(deviation as f64); + let (range, range_units) = utils::unitify(range as f64); + let (median, median_units) = utils::unitify(median as f64); - let (deviation, deviation_units) = if deviation >= 1000000000 { - (deviation as f64 / 1000000000.0, "s") - } else if deviation >= 1000000 { - (deviation as f64 / 1000000.0, "ms") - } else if deviation >= 1000 { - (deviation as f64 / 1000.0, "µs") - } else { - (deviation as f64, "ns") - }; - println!("\nAverage timing: {avg}{avg_units} ± {deviation}{deviation_units}"); + printdoc! {" + \rAverage timing: {avg}{avg_units} + | + |-- Std. Deviation: {deviation}{deviation_units} + |-- Min: {min}{min_units} + |-- Median: {median}{median_units} + |-- Max: {max}{max_units} + |-- Range: {range}{range_units} + "} } Subcommands::Input { day } => { println!("{}", fetcher::fetch_input(auth, day).await); diff --git a/cli/src/utils.rs b/cli/src/utils.rs new file mode 100644 index 0000000..e197567 --- /dev/null +++ b/cli/src/utils.rs @@ -0,0 +1,11 @@ +pub fn unitify(num: f64) -> (f64, &'static str) { + if num >= 1000000000.0 { + (num / 1000000000.0, "s") + } else if num >= 1000000.0 { + (num / 1000000.0, "ms") + } else if num >= 1000.0 { + (num / 1000.0, "µs") + } else { + (num, "ns") + } +} \ No newline at end of file diff --git a/lib/src/day16/mod.rs b/lib/src/day16/mod.rs index 2ea8959..a72bafe 100644 --- a/lib/src/day16/mod.rs +++ b/lib/src/day16/mod.rs @@ -4,13 +4,13 @@ use crate::utils::Day; pub struct Beam { pub position: Position, - pub direction: Direction + pub direction: Direction, } #[derive(Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] pub struct Position { pub x: usize, - pub y: usize + pub y: usize, } #[derive(Clone, PartialEq, Debug)] @@ -18,7 +18,7 @@ pub enum Direction { Up, Down, Right, - Left + Left, } #[derive(Debug, Default)] @@ -28,22 +28,35 @@ pub struct Day16 { impl Day for Day16 { fn part1(&mut self) -> String { - let grid = self.input.lines().map(|l| l.chars().collect_vec()).collect_vec(); + let grid = self + .input + .lines() + .map(|l| l.chars().collect_vec()) + .collect_vec(); let mut heated = Vec::<(Position, Direction)>::new(); - let mut beams = Vec::from(const { [Beam { position: Position { x: 0, y: 0 }, direction: Direction::Right }] }); + let mut beams = Vec::from( + const { + [Beam { + position: Position { x: 0, y: 0 }, + direction: Direction::Right, + }] + }, + ); while beams.len() > 0 { - // Step all beams for mut beam in std::mem::take(&mut beams).into_iter() { - let already_heated = heated.iter().filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction).collect_vec(); + let already_heated = heated + .iter() + .filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction) + .collect_vec(); if already_heated.len() == 0 { heated.push((beam.position.clone(), beam.direction.clone())); } else { continue; } - + match grid[beam.position.y][beam.position.x] { '/' => match beam.direction { Direction::Up => beam.direction = Direction::Right, @@ -59,29 +72,63 @@ impl Day for Day16 { }, '|' => match beam.direction { Direction::Left | Direction::Right => { - if beam.position.y != 0 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y - 1 }, direction: Direction::Up }) } - if beam.position.y != grid.len() - 1 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y + 1 }, direction: Direction::Down }) } - continue - }, - _ => () + if beam.position.y != 0 { + beams.push(Beam { + position: Position { + x: beam.position.x, + y: beam.position.y - 1, + }, + direction: Direction::Up, + }) + } + if beam.position.y != grid.len() - 1 { + beams.push(Beam { + position: Position { + x: beam.position.x, + y: beam.position.y + 1, + }, + direction: Direction::Down, + }) + } + continue; + } + _ => (), }, '-' => match beam.direction { Direction::Up | Direction::Down => { - if beam.position.x != 0 { beams.push(Beam { position: Position { x: beam.position.x - 1, y: beam.position.y }, direction: Direction::Left }) } - if beam.position.x != grid[0].len() - 1 { beams.push(Beam { position: Position { x: beam.position.x + 1, y: beam.position.y }, direction: Direction::Right }) } - continue - }, - _ => () + if beam.position.x != 0 { + beams.push(Beam { + position: Position { + x: beam.position.x - 1, + y: beam.position.y, + }, + direction: Direction::Left, + }) + } + if beam.position.x != grid[0].len() - 1 { + beams.push(Beam { + position: Position { + x: beam.position.x + 1, + y: beam.position.y, + }, + direction: Direction::Right, + }) + } + continue; + } + _ => (), }, - _ => () + _ => (), } match beam.direction { Direction::Up if beam.position.y != 0 => beam.position.y -= 1, Direction::Down if beam.position.y != grid.len() - 1 => beam.position.y += 1, - Direction::Right if beam.position.x != grid[0].len() - 1 => beam.position.x += 1, + Direction::Right if beam.position.x != grid[0].len() - 1 => { + beam.position.x += 1 + } Direction::Left if beam.position.x != 0 => beam.position.x -= 1, - _ => continue // If it hit the wall, skull issue, don't add it back to the vec + _ => continue, // If it hit the wall, skull issue, don't add it back to the vec } beams.push(beam); @@ -98,38 +145,58 @@ impl Day for Day16 { // .join("\n") // ); - heated.into_iter().map(|(pos, _)| pos).sorted().dedup().count().to_string() + heated + .into_iter() + .map(|(pos, _)| pos) + .sorted() + .dedup() + .count() + .to_string() } // TODO Don't do this the stupid way, use DP/caching lol fn part2(&mut self) -> String { - let grid = self.input.lines().map(|l| l.chars().collect_vec()).collect_vec(); + let grid = self + .input + .lines() + .map(|l| l.chars().collect_vec()) + .collect_vec(); let mut max = 0usize; - + for y in 0..grid.len() { for x in 0..grid[0].len() { - let dir = - if x == 0 { Direction::Left } - else if x == grid[0].len() - 1 { Direction::Right } - else if y == 0 { Direction::Down } - else if y == grid.len() - 1 { Direction::Up } - else { continue }; + let dir = if x == 0 { + Direction::Left + } else if x == grid[0].len() - 1 { + Direction::Right + } else if y == 0 { + Direction::Down + } else if y == grid.len() - 1 { + Direction::Up + } else { + continue; + }; let mut heated = Vec::<(Position, Direction)>::new(); - let mut beams = Vec::from([Beam { position: Position { x, y }, direction: dir }]); + let mut beams = Vec::from([Beam { + position: Position { x, y }, + direction: dir, + }]); while beams.len() > 0 { - // Step all beams for mut beam in std::mem::take(&mut beams).into_iter() { - let already_heated = heated.iter().filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction).collect_vec(); + let already_heated = heated + .iter() + .filter(|(pos, dir)| pos == &beam.position && dir == &beam.direction) + .collect_vec(); if already_heated.len() == 0 { heated.push((beam.position.clone(), beam.direction.clone())); } else { continue; } - + match grid[beam.position.y][beam.position.x] { '/' => match beam.direction { Direction::Up => beam.direction = Direction::Right, @@ -145,29 +212,65 @@ impl Day for Day16 { }, '|' => match beam.direction { Direction::Left | Direction::Right => { - if beam.position.y != 0 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y - 1 }, direction: Direction::Up }) } - if beam.position.y != grid.len() - 1 { beams.push(Beam { position: Position { x: beam.position.x, y: beam.position.y + 1 }, direction: Direction::Down }) } - continue - }, - _ => () + if beam.position.y != 0 { + beams.push(Beam { + position: Position { + x: beam.position.x, + y: beam.position.y - 1, + }, + direction: Direction::Up, + }) + } + if beam.position.y != grid.len() - 1 { + beams.push(Beam { + position: Position { + x: beam.position.x, + y: beam.position.y + 1, + }, + direction: Direction::Down, + }) + } + continue; + } + _ => (), }, '-' => match beam.direction { Direction::Up | Direction::Down => { - if beam.position.x != 0 { beams.push(Beam { position: Position { x: beam.position.x - 1, y: beam.position.y }, direction: Direction::Left }) } - if beam.position.x != grid[0].len() - 1 { beams.push(Beam { position: Position { x: beam.position.x + 1, y: beam.position.y }, direction: Direction::Right }) } - continue - }, - _ => () + if beam.position.x != 0 { + beams.push(Beam { + position: Position { + x: beam.position.x - 1, + y: beam.position.y, + }, + direction: Direction::Left, + }) + } + if beam.position.x != grid[0].len() - 1 { + beams.push(Beam { + position: Position { + x: beam.position.x + 1, + y: beam.position.y, + }, + direction: Direction::Right, + }) + } + continue; + } + _ => (), }, - _ => () + _ => (), } match beam.direction { Direction::Up if beam.position.y != 0 => beam.position.y -= 1, - Direction::Down if beam.position.y != grid.len() - 1 => beam.position.y += 1, - Direction::Right if beam.position.x != grid[0].len() - 1 => beam.position.x += 1, + Direction::Down if beam.position.y != grid.len() - 1 => { + beam.position.y += 1 + } + Direction::Right if beam.position.x != grid[0].len() - 1 => { + beam.position.x += 1 + } Direction::Left if beam.position.x != 0 => beam.position.x -= 1, - _ => continue // If it hit the wall, skull issue, don't add it back to the vec + _ => continue, // If it hit the wall, skull issue, don't add it back to the vec } beams.push(beam); @@ -184,8 +287,15 @@ impl Day for Day16 { // .join("\n") // ); - let result = heated.into_iter().map(|(pos, _)| pos).sorted().dedup().count(); - if max < result { max = result; } + let result = heated + .into_iter() + .map(|(pos, _)| pos) + .sorted() + .dedup() + .count(); + if max < result { + max = result; + } } }