diff --git a/README.md b/README.md index 391395e..0b38718 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Timings are given as: [lower-bound **best-estimate** upper-bound] | 02 | [2.0386 ms **2.0483 ms** 2.0584 ms] | [2.0823 ms **2.0918 ms** 2.1015 ms] | | 03 | [45.711 µs **45.937 µs** 46.177 µs] | [267.18 µs **267.95 µs** 268.75 µs] | | 04 | [143.40 µs **144.00 µs** 144.73 µs] | [1.6165 ms **1.6258 ms** 1.6355 ms] | +| 05 | [187.25 µs **188.93 µs** 190.74 µs] | [63.809 µs **64.204 µs** 64.606 µs] | ## Profiling diff --git a/src/day05/input/test1.txt b/src/day05/input/test1.txt new file mode 100644 index 0000000..2e9078d --- /dev/null +++ b/src/day05/input/test1.txt @@ -0,0 +1,11 @@ +3-5 +10-14 +16-20 +12-18 + +1 +5 +8 +11 +17 +32 diff --git a/src/day05/mod.rs b/src/day05/mod.rs new file mode 100644 index 0000000..c791be1 --- /dev/null +++ b/src/day05/mod.rs @@ -0,0 +1,125 @@ +use std::{fmt::Display, str::FromStr}; + +use color_eyre::{ + Result, + eyre::{Error, eyre}, +}; +use tracing::{debug, instrument}; + +pub const INPUT: &str = include_str!("input/input.txt"); + +#[derive(Clone, Debug)] +struct FreshRange(pub std::ops::RangeInclusive); + +impl FromStr for FreshRange { + type Err = Error; + + fn from_str(s: &str) -> Result { + let mut parts = s.split('-'); + let start = parts + .next() + .ok_or(eyre!("Invalid fresh range: no start"))? + .parse::()?; + let end = parts + .next() + .ok_or(eyre!("Invalid fresh range: no end"))? + .parse::()?; + if parts.next().is_some() { + return Err(eyre!("Invalid fresh range: too many parts")); + } + Ok(FreshRange(start..=end)) + } +} + +impl Display for FreshRange { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}-{}", self.0.start(), self.0.end()) + } +} + +#[instrument(skip(input))] +pub fn part1(input: &str) -> Result { + let mut processing_ranges = true; + let mut fresh_ranges = Vec::new(); + let mut fresh_ingredients = 0; + for line in input.trim().lines() { + if line.is_empty() { + processing_ranges = false; + continue; + } + if processing_ranges { + let range = line.parse::()?; + debug!(range = %range); + fresh_ranges.push(range); + } else { + let ingredient = line.parse::()?; + debug!(ingredient); + for range in &mut fresh_ranges { + if range.0.contains(&ingredient) { + fresh_ingredients += 1; + debug!(fresh_ingredients, "fresh!"); + break; + } + } + } + } + Ok(fresh_ingredients) +} + +#[instrument(skip(input))] +pub fn part2(input: &str) -> Result { + let mut fresh_ranges: Vec> = Vec::new(); + for line in input.trim().lines() { + if line.is_empty() { + break; + } + let range = line.parse::()?; + let mut overlap_range = range.clone(); + debug!(range = %range); + for range_slot in fresh_ranges.iter_mut() { + if let Some(existing_range) = range_slot { + if overlap_range.0.end() < existing_range.0.start() + || overlap_range.0.start() > existing_range.0.end() + { + continue; + } + let start = *overlap_range.0.start().min(existing_range.0.start()); + let end = *overlap_range.0.end().max(existing_range.0.end()); + if start <= end { + overlap_range = FreshRange(start..=end); + debug!(overlap_range = %overlap_range, existing_range = %existing_range, "merging existing range"); + *range_slot = None; // this existing range is now completely merged with the current range + } + } + } + fresh_ranges.push(Some(overlap_range)); + } + Ok(fresh_ranges + .iter() + .flatten() + .map(|r| (r.0.end() - r.0.start() + 1) as usize) + .sum()) +} + +#[cfg(test)] +mod tests { + use super::*; + use test_log::test; + + const TEST_INPUT1: &str = include_str!("input/test1.txt"); + + #[test] + fn test_part1() { + assert_eq!(part1(TEST_INPUT1).unwrap(), 3); + } + + #[test] + fn test_part2() { + assert_eq!(part2(TEST_INPUT1).unwrap(), 14); + } + + #[test] + fn test_part2_triple_overlap() { + assert_eq!(part2("3-4\n2-5\n1-6").unwrap(), 6); + } +} diff --git a/src/days.rs b/src/days.rs index 60bf563..5e689bc 100644 --- a/src/days.rs +++ b/src/days.rs @@ -9,6 +9,7 @@ macro_rules! all_days { 2 => day02, 3 => day03, 4 => day04, + 5 => day05, } }; } diff --git a/src/lib.rs b/src/lib.rs index d0e2c31..3393a19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,4 +2,5 @@ pub mod day01; pub mod day02; pub mod day03; pub mod day04; +pub mod day05; pub mod days;