diff --git a/2024/Cargo.lock b/2024/Cargo.lock index 2b9599c..e6976cb 100644 --- a/2024/Cargo.lock +++ b/2024/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -143,6 +143,21 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "day-03" +version = "0.1.0" +dependencies = [ + "divan", + "itertools", + "miette", + "nom", + "regex", + "rstest", + "test-log", + "tracing", + "tracing-subscriber", +] + [[package]] name = "divan" version = "0.1.16" diff --git a/2024/day-03/Cargo.toml b/2024/day-03/Cargo.toml new file mode 100644 index 0000000..70017cc --- /dev/null +++ b/2024/day-03/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "day-03" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools.workspace = true +nom.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +miette.workspace = true +regex = "1.11" + +[dev-dependencies] +divan.workspace = true +rstest.workspace = true +test-log.workspace = true + +[[bench]] +name = "day-03-bench" +path = "benches/benchmarks.rs" +harness = false diff --git a/2024/day-03/benches/benchmarks.rs b/2024/day-03/benches/benchmarks.rs new file mode 100644 index 0000000..a883666 --- /dev/null +++ b/2024/day-03/benches/benchmarks.rs @@ -0,0 +1,21 @@ +use day_03::*; + +fn main() { + divan::main(); +} + +#[divan::bench] +fn part1() { + part1::process(divan::black_box(include_str!( + "../input1.txt", + ))) + .unwrap(); +} + +#[divan::bench] +fn part2() { + part2::process(divan::black_box(include_str!( + "../input2.txt", + ))) + .unwrap(); +} diff --git a/2024/day-03/src/bin/part1.rs b/2024/day-03/src/bin/part1.rs new file mode 100644 index 0000000..715099f --- /dev/null +++ b/2024/day-03/src/bin/part1.rs @@ -0,0 +1,12 @@ +use day_03::part1::process; +use miette::{Context, Result}; + +#[tracing::instrument] +fn main() -> Result<()> { + tracing_subscriber::fmt::init(); + + let file = include_str!("../../input1.txt"); + let result = process(file).context("process part 1")?; + println!("{}", result); + Ok(()) +} diff --git a/2024/day-03/src/bin/part2.rs b/2024/day-03/src/bin/part2.rs new file mode 100644 index 0000000..f519877 --- /dev/null +++ b/2024/day-03/src/bin/part2.rs @@ -0,0 +1,12 @@ +use day_03::part2::process; +use miette::{Context, Result}; + +#[tracing::instrument] +fn main() -> Result<()> { + tracing_subscriber::fmt::init(); + + let file = include_str!("../../input2.txt"); + let result = process(file).context("process part 2")?; + println!("{}", result); + Ok(()) +} diff --git a/2024/day-03/src/lib.rs b/2024/day-03/src/lib.rs new file mode 100644 index 0000000..faaf542 --- /dev/null +++ b/2024/day-03/src/lib.rs @@ -0,0 +1,2 @@ +pub mod part1; +pub mod part2; diff --git a/2024/day-03/src/part1.rs b/2024/day-03/src/part1.rs new file mode 100644 index 0000000..6f70fa2 --- /dev/null +++ b/2024/day-03/src/part1.rs @@ -0,0 +1,62 @@ +use std::num::ParseIntError; + +use miette::Result; +use regex::Regex; + +#[derive(Debug)] +struct Multiplication(usize, usize); + +impl Multiplication { + fn calculate(&self) -> usize { + self.0 * self.1 + } +} + +impl TryFrom<(T, U)> for Multiplication +where + T: Into, + U: Into, +{ + type Error = ParseIntError; + fn try_from(value: (T, U)) -> std::result::Result { + Ok(Self( + value.0.into().parse::()?, + value.1.into().parse::()?, + )) + } +} + +fn extract_multiplications<'a>( + line: &'a str, + re: &'a Regex, +) -> impl Iterator + 'a { + re.captures_iter(line).filter_map(|caps| { + let first = caps.get(1)?.as_str().to_owned(); + let second = caps.get(2)?.as_str().to_owned(); + Multiplication::try_from((first, second)).ok() + }) +} + +#[tracing::instrument] +pub fn process(input: &str) -> Result { + let re = Regex::new(r"mul\((\d{1,3}),(\d{1,3})\)").unwrap(); + let result = input + .lines() + .flat_map(|line| extract_multiplications(line, &re)) + .map(|mult| mult.calculate()) + .sum(); + Ok(result) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process() -> Result<()> { + let input = "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))"; + let result = 161; + assert_eq!(process(input)?, result); + Ok(()) + } +} diff --git a/2024/day-03/src/part2.rs b/2024/day-03/src/part2.rs new file mode 100644 index 0000000..cbcdee4 --- /dev/null +++ b/2024/day-03/src/part2.rs @@ -0,0 +1,21 @@ +use miette::Result; + +#[tracing::instrument] +pub fn process(input: &str) -> Result { + todo!("day xx - part 2"); + Ok(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_process() -> Result<()> { + let input = ""; + todo!("haven't built test yet"); + let result = 0; + assert_eq!(process(input)?, result); + Ok(()) + } +}