This commit is contained in:
nora 2023-12-03 16:22:39 +01:00
parent 2ed587b9d1
commit adc39acadc
8 changed files with 36 additions and 150 deletions

View file

@ -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"

View file

@ -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;
}

View file

@ -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<u32, GearRatio> =
let mut gears: FxHashMap<u64, GearRatio> =
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::<u64>().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);
}
}
}

View file

@ -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<u32, Vec<u64>> = FxHashMap::default();
let mut gears: FxHashMap<u64, Vec<u64>> = 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::<u64>().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);
}
}
}

View file

@ -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<u64> {
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<usize> {
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::<String>();
let mut prev2 = empty_border.as_str();
let mut prev1 = input.lines().next().unwrap();
let mut gears: FxHashMap<u32, GearRatio> =
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::<u64>().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()
}

View file

@ -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<u32, GearRatio> =
let mut gears: FxHashMap<u64, GearRatio> =
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::<u64>().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);
}
}
}

View file

@ -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<u32, Vec<u64>> =
let mut gears: FxHashMap<u64, Vec<u64>> =
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::<u64>().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);
}
}
}

7
Cargo.lock generated
View file

@ -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"