diff --git a/2023/day03/Cargo.toml b/2023/day03/Cargo.toml index d83c877..41300e0 100644 --- a/2023/day03/Cargo.toml +++ b/2023/day03/Cargo.toml @@ -10,6 +10,7 @@ nom.workspace = true helper.workspace = true divan.workspace = true rustc-hash = "1.1.0" +smallvec = "1.11.2" [[bench]] name = "benches" diff --git a/2023/day03/src/lib.rs b/2023/day03/src/lib.rs index 5dc8e23..580e3ef 100644 --- a/2023/day03/src/lib.rs +++ b/2023/day03/src/lib.rs @@ -1,7 +1,6 @@ mod p2basic; mod p2bytes; mod p2faster_hash; -mod p2less_alloc; mod p2less_branching; mod p2with_capacity; @@ -22,7 +21,6 @@ helper::define_variants! { basic => crate::p2basic::part2; faster_hash => crate::p2faster_hash::part2; with_capacity => crate::p2with_capacity::part2; - less_alloc => crate::p2less_alloc::part2; less_branching => crate::p2less_branching::part2; bytes => crate::p2bytes::part2; } diff --git a/2023/day03/src/p2bytes.rs b/2023/day03/src/p2bytes.rs index 2e6f8bf..6ac046f 100644 --- a/2023/day03/src/p2bytes.rs +++ b/2023/day03/src/p2bytes.rs @@ -50,7 +50,7 @@ pub fn part2(input: &str) -> u64 { let mut prev2 = empty_border.as_str(); let mut prev1 = input.lines().next().unwrap(); - let mut gears: FxHashMap = + let mut gears: FxHashMap = FxHashMap::with_capacity_and_hasher(1000, BuildHasherDefault::default()); for (line_number, next) in input @@ -59,6 +59,7 @@ pub fn part2(input: &str) -> u64 { .chain(Some(empty_border.as_str())) .enumerate() { + assert!(line_number < u32::MAX as _); let mut numbers = prev1.bytes().enumerate().peekable(); while let Some((start, c)) = numbers.next() { if c.is_ascii_digit() { @@ -70,26 +71,23 @@ pub fn part2(input: &str) -> u64 { let box_bounds = (start.saturating_sub(1))..std::cmp::min(end + 2, len); let number = prev1[start..=end].parse::().unwrap(); - let mut push = |key: (usize, usize)| { + let mut push = |line, pos| { gears - .entry(((key.1 as u32) << 16) | (key.0 as u32)) + .entry((((box_bounds.start + pos) as u64) << 32) | (line as u64)) .or_default() .push(number) }; if let Some(position) = contains_gear(&prev2[box_bounds.clone()]) { - let key = (line_number - 1, box_bounds.start + position); - push(key); + push(line_number - 1, position); } if let Some(position) = contains_gear(&prev1[box_bounds.clone()]) { - let key = (line_number, box_bounds.start + position); - push(key); + push(line_number, position); } if let Some(position) = contains_gear(&next[box_bounds.clone()]) { - let key = (line_number + 1, box_bounds.start + position); - push(key); + push(line_number + 1, position); } } } diff --git a/2023/day03/src/p2faster_hash.rs b/2023/day03/src/p2faster_hash.rs index 228e9d9..14a7887 100644 --- a/2023/day03/src/p2faster_hash.rs +++ b/2023/day03/src/p2faster_hash.rs @@ -11,7 +11,7 @@ pub fn part2(input: &str) -> u64 { let mut prev2 = empty_border.as_str(); let mut prev1 = input.lines().next().unwrap(); - let mut gears: FxHashMap> = FxHashMap::default(); + let mut gears: FxHashMap> = FxHashMap::default(); for (line_number, next) in input .lines() @@ -19,6 +19,7 @@ pub fn part2(input: &str) -> u64 { .chain(Some(empty_border.as_str())) .enumerate() { + assert!(line_number < u32::MAX as _); let mut numbers = prev1.char_indices().peekable(); while let Some((start, c)) = numbers.next() { if c.is_ascii_digit() { @@ -30,26 +31,23 @@ pub fn part2(input: &str) -> u64 { let box_bounds = (start.saturating_sub(1))..std::cmp::min(end + 2, len); let number = prev1[start..=end].parse::().unwrap(); - let mut push = |key: (usize, usize)| { + let mut push = |line, pos| { gears - .entry(((key.1 as u32) << 16) | (key.0 as u32)) + .entry((((box_bounds.start + pos) as u64) << 32) | (line as u64)) .or_default() .push(number) }; if let Some(position) = contains_gear(&prev2[box_bounds.clone()]) { - let key = (line_number - 1, box_bounds.start + position); - push(key); + push(line_number - 1, position); } if let Some(position) = contains_gear(&prev1[box_bounds.clone()]) { - let key = (line_number, box_bounds.start + position); - push(key); + push(line_number, position); } if let Some(position) = contains_gear(&next[box_bounds.clone()]) { - let key = (line_number + 1, box_bounds.start + position); - push(key); + push(line_number + 1, position); } } } diff --git a/2023/day03/src/p2less_alloc.rs b/2023/day03/src/p2less_alloc.rs deleted file mode 100644 index d022f87..0000000 --- a/2023/day03/src/p2less_alloc.rs +++ /dev/null @@ -1,112 +0,0 @@ -use std::hash::BuildHasherDefault; - -use rustc_hash::FxHashMap; - -/// States: -/// - `=0`` -> 0 elements -/// - `>0, HIGH 0` -> 1 element -/// - `>0, HIGH 1` -> 2 elements -/// - `=MAX` -> >2 elements -#[derive(Debug, Default)] -struct GearRatio(u64); - -const HIGH: u64 = 1 << 63; -impl GearRatio { - fn push(&mut self, value: u64) { - if self.0 == 0 { - self.0 = value; - } else if (self.0 & HIGH) == 0 { - self.0 *= value; - self.0 |= HIGH; - } else { - self.0 = u64::MAX; - } - } - fn get(&self) -> Option { - if self.0 > 0 && self.0 != u64::MAX && (self.0 & HIGH) > 1 { - Some(self.0 & (HIGH - 1)) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use super::GearRatio; - - #[test] - fn gear_ratio() { - let mut r = GearRatio::default(); - assert_eq!(r.get(), None); - r.push(2); - assert_eq!(r.get(), None); - r.push(3); - eprintln!("{:b}", r.0); - assert_eq!(r.get(), Some(6)); - r.push(1); - assert_eq!(r.get(), None); - } -} - -pub fn part2(input: &str) -> u64 { - fn contains_gear(s: &str) -> Option { - s.chars().position(|c| !c.is_ascii_digit() && c != '.') - } - - let len = input.lines().next().unwrap().len(); - let empty_border = std::iter::repeat('.').take(len).collect::(); - - let mut prev2 = empty_border.as_str(); - let mut prev1 = input.lines().next().unwrap(); - - let mut gears: FxHashMap = - FxHashMap::with_capacity_and_hasher(1000, BuildHasherDefault::default()); - - for (line_number, next) in input - .lines() - .skip(1) - .chain(Some(empty_border.as_str())) - .enumerate() - { - let mut numbers = prev1.char_indices().peekable(); - while let Some((start, c)) = numbers.next() { - if c.is_ascii_digit() { - let mut end = start; - while let Some((idx, '0'..='9')) = numbers.next() { - end = idx; - } - - let box_bounds = (start.saturating_sub(1))..std::cmp::min(end + 2, len); - let number = prev1[start..=end].parse::().unwrap(); - - let mut push = |key: (usize, usize)| { - gears - .entry(((key.1 as u32) << 16) | (key.0 as u32)) - .or_default() - .push(number) - }; - - if let Some(position) = contains_gear(&prev2[box_bounds.clone()]) { - let key = (line_number - 1, box_bounds.start + position); - push(key); - } - - if let Some(position) = contains_gear(&prev1[box_bounds.clone()]) { - let key = (line_number, box_bounds.start + position); - push(key); - } - - if let Some(position) = contains_gear(&next[box_bounds.clone()]) { - let key = (line_number + 1, box_bounds.start + position); - push(key); - } - } - } - - prev2 = prev1; - prev1 = next; - } - - gears.values().filter_map(|v| v.get()).sum() -} diff --git a/2023/day03/src/p2less_branching.rs b/2023/day03/src/p2less_branching.rs index c12dfa4..dcda6d7 100644 --- a/2023/day03/src/p2less_branching.rs +++ b/2023/day03/src/p2less_branching.rs @@ -50,7 +50,7 @@ pub fn part2(input: &str) -> u64 { let mut prev2 = empty_border.as_str(); let mut prev1 = input.lines().next().unwrap(); - let mut gears: FxHashMap = + let mut gears: FxHashMap = FxHashMap::with_capacity_and_hasher(1000, BuildHasherDefault::default()); for (line_number, next) in input @@ -59,6 +59,7 @@ pub fn part2(input: &str) -> u64 { .chain(Some(empty_border.as_str())) .enumerate() { + assert!(line_number < u32::MAX as _); let mut numbers = prev1.char_indices().peekable(); while let Some((start, c)) = numbers.next() { if c.is_ascii_digit() { @@ -70,26 +71,23 @@ pub fn part2(input: &str) -> u64 { let box_bounds = (start.saturating_sub(1))..std::cmp::min(end + 2, len); let number = prev1[start..=end].parse::().unwrap(); - let mut push = |key: (usize, usize)| { + let mut push = |line, pos| { gears - .entry(((key.1 as u32) << 16) | (key.0 as u32)) + .entry((((box_bounds.start + pos) as u64) << 32) | (line as u64)) .or_default() .push(number) }; if let Some(position) = contains_gear(&prev2[box_bounds.clone()]) { - let key = (line_number - 1, box_bounds.start + position); - push(key); + push(line_number - 1, position); } if let Some(position) = contains_gear(&prev1[box_bounds.clone()]) { - let key = (line_number, box_bounds.start + position); - push(key); + push(line_number, position); } if let Some(position) = contains_gear(&next[box_bounds.clone()]) { - let key = (line_number + 1, box_bounds.start + position); - push(key); + push(line_number + 1, position); } } } diff --git a/2023/day03/src/p2with_capacity.rs b/2023/day03/src/p2with_capacity.rs index 9e5e605..a07682e 100644 --- a/2023/day03/src/p2with_capacity.rs +++ b/2023/day03/src/p2with_capacity.rs @@ -13,7 +13,7 @@ pub fn part2(input: &str) -> u64 { let mut prev2 = empty_border.as_str(); let mut prev1 = input.lines().next().unwrap(); - let mut gears: FxHashMap> = + let mut gears: FxHashMap> = FxHashMap::with_capacity_and_hasher(1000, BuildHasherDefault::default()); for (line_number, next) in input @@ -22,6 +22,7 @@ pub fn part2(input: &str) -> u64 { .chain(Some(empty_border.as_str())) .enumerate() { + assert!(line_number < u32::MAX as _); let mut numbers = prev1.char_indices().peekable(); while let Some((start, c)) = numbers.next() { if c.is_ascii_digit() { @@ -33,26 +34,23 @@ pub fn part2(input: &str) -> u64 { let box_bounds = (start.saturating_sub(1))..std::cmp::min(end + 2, len); let number = prev1[start..=end].parse::().unwrap(); - let mut push = |key: (usize, usize)| { + let mut push = |line, pos| { gears - .entry(((key.1 as u32) << 16) | (key.0 as u32)) + .entry((((box_bounds.start + pos) as u64) << 32) | (line as u64)) .or_default() .push(number) }; if let Some(position) = contains_gear(&prev2[box_bounds.clone()]) { - let key = (line_number - 1, box_bounds.start + position); - push(key); + push(line_number - 1, position); } if let Some(position) = contains_gear(&prev1[box_bounds.clone()]) { - let key = (line_number, box_bounds.start + position); - push(key); + push(line_number, position); } if let Some(position) = contains_gear(&next[box_bounds.clone()]) { - let key = (line_number + 1, box_bounds.start + position); - push(key); + push(line_number + 1, position); } } } diff --git a/Cargo.lock b/Cargo.lock index f47ce2b..d412e0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,7 @@ dependencies = [ "helper", "nom", "rustc-hash", + "smallvec", ] [[package]] @@ -251,6 +252,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + [[package]] name = "strsim" version = "0.10.0"