This commit is contained in:
nora 2023-12-04 21:21:19 +01:00
parent 93af372574
commit b12e92a5cb
8 changed files with 238 additions and 10 deletions

View file

@ -9,6 +9,8 @@ edition = "2021"
nom.workspace = true nom.workspace = true
helper.workspace = true helper.workspace = true
divan.workspace = true divan.workspace = true
arrayvec = "0.7.4"
bstr = "1.8.0"
[[bench]] [[bench]]
name = "benches" name = "benches"

View file

@ -1,4 +1,7 @@
mod p2ascii;
mod p2cache; mod p2cache;
mod p2chunks;
mod p2no_alloc;
use helper::{Day, Variants}; use helper::{Day, Variants};
@ -11,11 +14,14 @@ struct Day04;
helper::define_variants! { helper::define_variants! {
day => crate::Day04; day => crate::Day04;
part1 { part1 {
basic => crate::part1, sample_count=1000; basic => crate::part1;
} }
part2 { part2 {
basic => crate::part2, sample_count=100; basic => crate::part2, sample_count=100;
cache => crate::p2cache::part2, sample_count=1000; cache => crate::p2cache::part2;
no_alloc => crate::p2no_alloc::part2;
ascii => crate::p2ascii::part2;
chunks => crate::p2chunks::part2;
} }
} }

43
2023/day04/src/p2ascii.rs Normal file
View file

@ -0,0 +1,43 @@
fn line_match_count(line: &str) -> usize {
let mut numbers = line.split(':').nth(1).unwrap().split("|");
let winning = numbers
.next()
.unwrap()
.split_ascii_whitespace()
.collect::<arrayvec::ArrayVec<_, 16>>();
let you_have = numbers.next().unwrap().split_ascii_whitespace();
you_have
.filter(|have| winning.iter().any(|w| w == have))
.count()
}
pub fn part2(input: &str) -> u64 {
let lines = input.lines().map(line_match_count).collect::<Vec<_>>();
let mut cache = vec![0; lines.len()];
let mut processed = 0;
// By iterating backwards, we ensure the cache is always populated for every subsequent line.
for (i, result) in lines.iter().copied().enumerate().rev() {
let before = processed;
processed += 1; // Every card gives us one card.
// Now, let's see how many cards this card will expand to.
for expand in (i + 1)..((i + 1) + result) {
#[cfg(debug_assertions)]
eprintln!(
"{} expands to {} which is worth {}",
i + 1,
expand + 1,
cache[expand]
);
// Since the value is bigger than i, it must be cached!
processed += cache[expand];
}
cache[i] = processed - before;
}
processed
}

View file

@ -0,0 +1,49 @@
fn line_match_count(line: &str) -> usize {
let mut numbers = line.split(':').nth(1).unwrap().split("|");
let winning = numbers
.next()
.unwrap()
.as_bytes()
// Chunks of double digit numbers with a leading space. We don't care about the trailing space.
.chunks_exact(3)
.collect::<arrayvec::ArrayVec<_, 16>>();
let you_have = numbers.next().unwrap().as_bytes().chunks_exact(3);
you_have
.filter(|have| {
winning.iter().any(|w| {
w == have
})
})
.count()
}
pub fn part2(input: &str) -> u64 {
let lines = input.lines().map(line_match_count).collect::<Vec<_>>();
let mut cache = vec![0; lines.len()];
let mut processed = 0;
// By iterating backwards, we ensure the cache is always populated for every subsequent line.
for (i, result) in lines.iter().copied().enumerate().rev() {
let before = processed;
processed += 1; // Every card gives us one card.
// Now, let's see how many cards this card will expand to.
for expand in (i + 1)..((i + 1) + result) {
#[cfg(debug_assertions)]
eprintln!(
"{} expands to {} which is worth {}",
i + 1,
expand + 1,
cache[expand]
);
// Since the value is bigger than i, it must be cached!
processed += cache[expand];
}
cache[i] = processed - before;
}
processed
}

View file

@ -0,0 +1,43 @@
fn line_match_count(line: &str) -> usize {
let mut numbers = line.split(':').nth(1).unwrap().split("|");
let winning = numbers
.next()
.unwrap()
.split_whitespace()
.collect::<arrayvec::ArrayVec<_, 16>>();
let you_have = numbers.next().unwrap().split_whitespace();
you_have
.filter(|have| winning.iter().any(|w| w == have))
.count()
}
pub fn part2(input: &str) -> u64 {
let lines = input.lines().map(line_match_count).collect::<Vec<_>>();
let mut cache = vec![0; lines.len()];
let mut processed = 0;
// By iterating backwards, we ensure the cache is always populated for every subsequent line.
for (i, result) in lines.iter().copied().enumerate().rev() {
let before = processed;
processed += 1; // Every card gives us one card.
// Now, let's see how many cards this card will expand to.
for expand in (i + 1)..((i + 1) + result) {
#[cfg(debug_assertions)]
eprintln!(
"{} expands to {} which is worth {}",
i + 1,
expand + 1,
cache[expand]
);
// Since the value is bigger than i, it must be cached!
processed += cache[expand];
}
cache[i] = processed - before;
}
processed
}

45
Cargo.lock generated
View file

@ -50,12 +50,29 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.4.1" version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bstr"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c"
dependencies = [
"memchr",
"regex-automata",
"serde",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.10" version = "4.4.10"
@ -137,6 +154,8 @@ dependencies = [
name = "day04" name = "day04"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"arrayvec",
"bstr",
"divan", "divan",
"helper", "helper",
"nom", "nom",
@ -236,6 +255,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
[[package]] [[package]]
name = "regex-lite" name = "regex-lite"
version = "0.1.5" version = "0.1.5"
@ -261,6 +286,26 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "serde"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.11.2" version = "1.11.2"

45
helper/src/ext.rs Normal file
View file

@ -0,0 +1,45 @@
use std::mem::MaybeUninit;
use nom::{character::complete::digit1, combinator::map, IResult};
pub fn integer(input: &str) -> IResult<&str, u64> {
map(digit1, |d: &str| d.parse::<u64>().unwrap())(input)
}
#[derive(Debug)]
pub struct CollectArrayError;
/// i will not use itertools i will not use itertools i will not use itertools i will not use itertools
pub trait IteratorExt: Iterator {
/// Collect an iterator into an array.
/// If `next` panics, collected items are leaked. Too bad!
fn collect_array<const N: usize>(&mut self) -> Result<[Self::Item; N], CollectArrayError> {
// SAFETY: Uninit is valid for MaybeUninit
let mut array: [MaybeUninit<Self::Item>; N] = unsafe { MaybeUninit::uninit().assume_init() };
for i in 0..array.len() {
array[i].write(self.next().ok_or(CollectArrayError)?);
}
if self.next().is_some() {
return Err(CollectArrayError);
}
// SAFETY: All elements have been initialized
Ok(array.map(|elem| unsafe { elem.assume_init() }))
}
}
impl<I: Iterator> IteratorExt for I {}
#[cfg(test)]
mod tests {
use crate::IteratorExt;
#[test]
fn collect_array() {
assert!([0, 1].into_iter().collect_array::<3>().is_err());
assert!([0, 1].into_iter().collect_array::<1>().is_err());
assert_eq!([0, 1].into_iter().collect_array().unwrap(), [0, 1]);
}
}

View file

@ -1,11 +1,10 @@
mod cmd; mod cmd;
mod ext;
use std::{borrow::Cow, fmt::Debug}; use std::{borrow::Cow, fmt::Debug};
use nom::{character::complete::digit1, combinator::map, IResult};
pub use self::cmd::main; pub use self::cmd::main;
pub use divan; pub use self::ext::*;
pub type Solution = fn(&str) -> u64; pub type Solution = fn(&str) -> u64;
@ -123,7 +122,7 @@ macro_rules! benchmarks {
#[macro_export] #[macro_export]
macro_rules! _bench_sample_count { macro_rules! _bench_sample_count {
(;$($tt:tt)*) => { (;$($tt:tt)*) => {
#[::divan::bench(sample_count = 10_000)] #[::divan::bench(sample_count = 5000)]
$($tt)* $($tt)*
}; };
($sample_count:expr; $($tt:tt)*) => { ($sample_count:expr; $($tt:tt)*) => {
@ -209,7 +208,3 @@ macro_rules! tests {
} }
}; };
} }
pub fn integer(input: &str) -> IResult<&str, u64> {
map(digit1, |d: &str| d.parse::<u64>().unwrap())(input)
}