diff --git a/7/Cargo.lock b/7/Cargo.lock index 5746643..a174188 100644 --- a/7/Cargo.lock +++ b/7/Cargo.lock @@ -2,11 +2,44 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "day7" version = "0.1.0" dependencies = [ "itertools", + "rayon", + "thread_local", ] [[package]] @@ -23,3 +56,39 @@ checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] diff --git a/7/Cargo.toml b/7/Cargo.toml index 3bc0606..f1bf891 100644 --- a/7/Cargo.toml +++ b/7/Cargo.toml @@ -5,3 +5,5 @@ edition = "2021" [dependencies] itertools = "0.13.0" +rayon = "1.10.0" +thread_local = "1.1.8" diff --git a/7/src/main.rs b/7/src/main.rs index c388fab..b3e8dc8 100644 --- a/7/src/main.rs +++ b/7/src/main.rs @@ -3,6 +3,8 @@ use std::io::{BufRead, BufReader, Lines}; use std::time::{Duration, Instant}; use itertools::Itertools; +use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use thread_local::ThreadLocal; // BOILERPLATE type InputIter = Lines>; @@ -36,7 +38,7 @@ fn main() { println!("Total duration: {}", duration_format(duration1 + duration2)); } -#[derive(Debug)] +#[derive(Debug, Clone)] struct Calibration { result: u64, numbers: Vec, @@ -55,9 +57,10 @@ impl From<&str> for Calibration { #[derive(Debug)] struct Calibrations { cals: Vec, + longest_cal: usize, } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] enum Operator { Add, Multiply, @@ -69,7 +72,7 @@ impl Operator { match self { Operator::Add => a + b, Operator::Multiply => a * b, - Operator::Concatenate => u64::pow(10, b.to_string().len() as u32) * a + b, + Operator::Concatenate => u64::pow(10, u64::ilog10(b) + 1) * a + b, } } } @@ -77,10 +80,55 @@ impl Operator { impl From> for Calibrations { fn from(input: Lines) -> Self { let mut cals = Vec::new(); + let mut longest_cal = 0; for line in input.map(|l| l.unwrap()) { - cals.push(line.as_str().into()); + let cal: Calibration = line.as_str().into(); + longest_cal = std::cmp::max(longest_cal, cal.numbers.len()); + cals.push(cal); } - Self { cals } + Self { cals, longest_cal } + } +} + +impl Calibrations { + fn make_operator_sets(operators: &[Operator], n_opers: usize) -> Vec>> { + (0..n_opers) + .map(|k| { + std::iter::repeat_n(operators.iter().map(|v| *v), k) + .multi_cartesian_product() + .collect() + }) + .collect() + } + fn check_oper_set(cal: &Calibration, oper_set: &Vec) -> bool { + let accum = oper_set + .iter() + .zip(cal.numbers.iter().skip(1)) + .fold(cal.numbers[0], |accum, (oper, val)| oper.exec(accum, *val)); + if accum == cal.result { + true + } else { + false + } + } + fn possible(&self, operators: &[Operator]) -> u64 { + let operator_sets = Calibrations::make_operator_sets(operators, self.longest_cal); + self.cals + .par_iter() + .map(|cal| { + let n_opers = cal.numbers.len() - 1; + let tl = ThreadLocal::new(); + if operator_sets[n_opers] + .par_iter() + .find_any(|oper_set| Self::check_oper_set(&cal, &oper_set)) + .is_some() + { + let cal_local = tl.get_or(|| cal.clone()); + return cal_local.result; + } + 0 + }) + .sum() } } @@ -88,56 +136,15 @@ impl From> for Calibrations { fn problem1(input: Lines) -> u64 { let cals = Calibrations::from(input); - // println!("{:?}", cals); - let mut sum = 0; - let operators = [Operator::Add, Operator::Multiply]; - - for cal in &cals.cals { - let n_opers = cal.numbers.len() - 1; - // println!("CAL: {:?} (opers: {})", cal, n_opers); - for oper_set in std::iter::repeat_n(operators.iter(), n_opers).multi_cartesian_product() { - // println!("operator set: {:?}", oper_set); - let mut accum = cal.numbers[0]; - for (i, oper) in oper_set.iter().enumerate() { - // println!("Testing {} {:?} {}", accum, oper, cal.numbers[i+1]); - accum = oper.exec(accum, cal.numbers[i + 1]); - } - if accum == cal.result { - sum += cal.result; - // println!("Matched!"); - break; - } - } - // println!("NO MATCHES"); - } - sum + cals.possible(&operators) } // PROBLEM 2 solution fn problem2(input: Lines) -> u64 { let cals = Calibrations::from(input); - let mut sum = 0; let operators = [Operator::Add, Operator::Multiply, Operator::Concatenate]; - for cal in &cals.cals { - let n_opers = cal.numbers.len() - 1; - // println!("CAL: {:?} (opers: {})", cal, n_opers); - for oper_set in std::iter::repeat_n(operators.iter(), n_opers).multi_cartesian_product() { - // println!("operator set: {:?}", oper_set); - let mut accum = cal.numbers[0]; - for (i, oper) in oper_set.iter().enumerate() { - // println!("Testing {} {:?} {}", accum, oper, cal.numbers[i+1]); - accum = oper.exec(accum, cal.numbers[i + 1]); - } - if accum == cal.result { - sum += cal.result; - // println!("Matched!"); - break; - } - } - // println!("NO MATCHES"); - } - sum + cals.possible(&operators) } #[cfg(test)]