this code is way too complicated

This commit is contained in:
nora 2024-12-09 21:09:45 +01:00
parent 7c9d2a2377
commit 6969604ec5

View file

@ -1,5 +1,3 @@
use std::iter;
use helper::{Day, Variants};
pub fn main() {
@ -15,6 +13,8 @@ helper::define_variants! {
}
part2 {
basic => crate::part2,sample_count=1;
index => crate::part2_index,sample_count=1000;
index_index => crate::part2_index_index;
}
}
@ -29,6 +29,8 @@ impl Day for Day09 {
}
fn part1(input: &str) -> u64 {
use std::iter;
let input = &input[0..input.len() - 1];
let mut blocks = Vec::new();
@ -74,6 +76,8 @@ fn part1(input: &str) -> u64 {
}
fn part2(input: &str) -> u64 {
use std::iter;
#[derive(Clone, Copy, Debug)]
enum Block {
File { id: u16, size: u8 },
@ -158,6 +162,304 @@ fn part2(input: &str) -> u64 {
.sum()
}
fn part2_index(input: &str) -> u64 {
use std::iter;
#[derive(Clone, Copy, Debug)]
enum Block {
File { id: u16, size: u8 },
Space,
}
#[expect(dead_code)]
fn print_blocks(blocks: &[Block]) {
for b in blocks {
match b {
Block::Space { .. } => eprint!("."),
Block::File { id, .. } => eprint!("{}", id),
}
}
eprintln!();
}
let input = &input[0..input.len() - 1];
let mut index = [const { Vec::<usize>::new() }; 10];
let mut blocks = Vec::<Block>::new();
let mut is_file = true;
let mut id = 0_u16;
for b in input.bytes() {
debug_assert!(b.is_ascii_digit());
let len = (b - b'0') as usize;
if is_file {
blocks.extend(iter::repeat_n(
Block::File {
id,
size: len as u8,
},
len,
));
id += 1;
} else {
if len > 0 {
index[len].push(blocks.len());
}
blocks.extend(iter::repeat_n(Block::Space, len));
}
is_file = !is_file;
}
index.iter_mut().for_each(|v| v.reverse());
let mut i = blocks.len() - 1;
// print_blocks(&blocks);
while i > 0 {
match blocks[i] {
Block::File { id: _, size } => {
let end = i + 1;
let size_usize = size as usize;
let possible_gaps = &mut index[size_usize..];
let gap = possible_gaps
.iter_mut()
.enumerate()
.filter(|(_, gaps)| gaps.last().is_some_and(|gap| *gap < i))
.min_by_key(|(_, gaps)| *gaps.last().unwrap());
match gap {
Some((gap_size, chosen_gap)) => {
let gap_size = gap_size + size_usize;
let chosen_gap = chosen_gap.pop().unwrap();
blocks.copy_within((end - size_usize)..end, chosen_gap);
blocks[(end - size_usize)..][..size_usize].fill(Block::Space);
i = i.saturating_sub(size_usize);
// add gap back to index
if gap_size > size_usize {
let remaining_size = gap_size - size_usize;
let remaining_index = chosen_gap + size_usize;
let bucket = &mut index[remaining_size];
let back_offset_insert =
bucket.iter().rev().position(|gap| *gap > remaining_index);
match back_offset_insert {
Some(back_offset_insert) => {
let len = bucket.len();
bucket.insert(len - back_offset_insert, remaining_index);
}
None => bucket.push(remaining_index),
}
}
// print_blocks(&blocks);
}
None => {
i = i.saturating_sub(size_usize);
}
}
}
Block::Space { .. } => {
i = i.saturating_sub(1);
}
}
}
// print_blocks(&blocks);
blocks
.iter()
.enumerate()
.filter_map(|(i, tile)| match tile {
Block::File { id, .. } => Some(*id as u64 * i as u64),
_ => None,
})
.sum()
}
fn part2_index_index(input: &str) -> u64 {
use std::iter;
#[derive(Clone, Copy, Debug)]
enum Block {
File { id: u16, size: u8 },
Space,
}
#[expect(dead_code)]
fn print_blocks(blocks: &[Block]) {
for b in blocks {
match b {
Block::Space { .. } => eprint!("."),
Block::File { id, .. } => eprint!("{}", id),
}
}
eprintln!();
}
let input = &input[0..input.len() - 1];
let mut index = [const { Vec::<usize>::new() }; 10];
let mut blocks = Vec::<Block>::with_capacity(input.len());
let mut is_file = true;
let mut id = 0_u16;
for b in input.bytes() {
debug_assert!(b.is_ascii_digit());
let len = (b - b'0') as usize;
if is_file {
blocks.extend(iter::repeat_n(
Block::File {
id,
size: len as u8,
},
len,
));
id += 1;
} else {
if len > 0 {
index[len].push(blocks.len());
}
blocks.extend(iter::repeat_n(Block::Space, len));
}
is_file = !is_file;
}
index.iter_mut().for_each(|v| v.reverse());
let mut min_index = [None::<(usize, usize)>; 10];
min_index[min_index.len() - 1] = index[min_index.len() - 1]
.last()
.copied()
.map(|gap| (index.len() - 1, gap));
for i in (1..(min_index.len() - 1)).rev() {
min_index[i] = match (index[i].last().copied(), min_index[i + 1]) {
(None, None) => None,
(Some(x), None) => Some((i, x)),
(None, Some(x)) => Some(x),
(Some(x), Some(y)) => {
if x < y.1 {
Some((i, x))
} else {
Some(y)
}
}
}
}
fn verify_min_index(index: &[Vec<usize>], min_index: &[Option<(usize, usize)>]) {
for bucket in 1..10 {
let actual_min_bucket = index[bucket..]
.iter()
.enumerate()
.filter_map(|(i, gaps)| gaps.last().map(|gap| (i + bucket, *gap)))
.min_by_key(|(_, gap)| *gap);
assert_eq!(min_index[bucket], actual_min_bucket, "for {bucket}");
}
}
let mut i = blocks.len() - 1;
// print_blocks(&blocks);
while i > 0 {
if cfg!(debug_assertions) {
verify_min_index(&index, &min_index);
}
match blocks[i] {
Block::File { id: _, size } => {
let end = i + 1;
let size_usize = size as usize;
let gap = min_index[size_usize];
match gap {
Some((_, chosen_gap)) if chosen_gap > i => {
i = i.saturating_sub(size_usize);
}
Some((gap_size, chosen_gap)) => {
let poppped_gap_offset = index[gap_size].pop().unwrap();
debug_assert_eq!(poppped_gap_offset, chosen_gap);
blocks.copy_within((end - size_usize)..end, chosen_gap);
blocks[(end - size_usize)..][..size_usize].fill(Block::Space);
i = i.saturating_sub(size_usize);
// add gap back to index
if gap_size > size_usize {
let remaining_size = gap_size - size_usize;
let remaining_index = chosen_gap + size_usize;
let bucket = &mut index[remaining_size];
let back_offset_insert =
bucket.iter().rev().position(|gap| *gap > remaining_index);
match back_offset_insert {
Some(back_offset_insert) => {
let len = bucket.len();
bucket.insert(len - back_offset_insert, remaining_index);
}
None => bucket.push(remaining_index),
}
}
// recompute min_index
if gap_size < index.len() - 1 {
min_index[gap_size] =
match (index[gap_size].last(), min_index[gap_size + 1]) {
(None, None) => None,
(Some(x), None) => Some((gap_size, *x)),
(None, Some(x)) => Some(x),
(Some(x), Some(y)) => {
if *x < y.1 {
Some((gap_size, *x))
} else {
Some(y)
}
}
}
} else {
min_index[gap_size] =
index[gap_size].last().map(|gap| (gap_size, *gap));
}
for i in (1..gap_size).rev() {
min_index[i] = match (index[i].last(), min_index[i + 1]) {
(None, None) => None,
(Some(x), None) => Some((i, *x)),
(None, Some(y)) => Some(y),
(Some(x), Some(y)) => {
if *x < y.1 {
Some((i, *x))
} else {
Some(y)
}
}
}
}
// print_blocks(&blocks);
}
None => {
i = i.saturating_sub(size_usize);
}
}
}
Block::Space { .. } => {
i = i.saturating_sub(1);
}
}
}
// print_blocks(&blocks);
blocks
.iter()
.enumerate()
.filter_map(|(i, tile)| match tile {
Block::File { id, .. } => Some(*id as u64 * i as u64),
_ => None,
})
.sum()
}
helper::tests! {
day09 Day09;
part1 {
@ -166,7 +468,7 @@ helper::tests! {
}
part2 {
small => 2858;
default => 0;
default => 6467290479134;
}
}
helper::benchmarks! {}