day5: bugfixes for rangeset

This commit is contained in:
2025-12-05 01:09:32 -08:00
parent 252bbf3cda
commit 73765e857a

View File

@@ -1,4 +1,4 @@
use std::cmp::{max, min}; use std::cmp::max;
use std::fmt::Debug; use std::fmt::Debug;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
@@ -43,45 +43,47 @@ impl RangeSet {
Self { ranges: Vec::new() } Self { ranges: Vec::new() }
} }
fn simplify(&mut self) { fn simplify(&mut self) {
// sort the ranges by start
if self.ranges.len() < 2 { if self.ranges.len() < 2 {
return; return;
} }
let mut modified = true; let mut modified = true;
while modified { while modified {
let mut new_ranges = Vec::new();
modified = false; modified = false;
// sort the ranges by start
self.ranges.sort_by_key(|s| s.start); self.ranges.sort_by_key(|s| s.start);
for chunk in self.ranges.chunks(2) { let mut new_ranges = Vec::new();
if chunk.len() == 1 { let mut pos = 1;
new_ranges.push(chunk[0].clone());
continue; while pos <= self.ranges.len() {
// part of a disgusting hack to avoid skipping or double adding at the end
if pos == self.ranges.len() {
pos -= 1;
} }
let (l, r) = (&chunk[0], &chunk[1]); let (l, r) = (&self.ranges[pos - 1], &self.ranges[pos]);
if r.start <= l.end + 1 { if r.start <= l.end + 1 {
modified = true; modified = true;
new_ranges.push((min(r.start, l.start), max(l.end, r.end)).into()) new_ranges.push((l.start, max(l.end, r.end)).into());
} else { // if we merged, skip checking the next range to avoid double-adding it
new_ranges.push(l.clone()); // this screws us up at the very end, so need the disgusting hack above
new_ranges.push(r.clone()); pos += 2;
} } else if pos < self.ranges.len() - 1 {
} // keep only the lhs if we didn't merge
if !self.ranges.len().is_multiple_of(2) && new_ranges.len() >= 2 { new_ranges.push(l.clone());
let (l, r) = (new_ranges.pop().unwrap(), new_ranges.pop().unwrap()); pos += 1;
if r.start <= l.end + 1 && r.end >= l.start - 1 {
modified = true;
new_ranges.push((min(r.start, l.start), max(l.end, r.end)).into())
} else { } else {
// at the end, keep both sides, otherwise the rhs gets excluded (if it didn't merge)
new_ranges.push(l.clone()); new_ranges.push(l.clone());
new_ranges.push(r.clone()); new_ranges.push(r.clone());
pos += 2;
} }
} }
self.ranges = new_ranges; self.ranges = new_ranges;
} }
} }
fn add(&mut self, s: Span) { fn add(&mut self, s: Span) {
self.ranges.push(s); self.ranges.push(s);
self.simplify()
} }
} }
@@ -111,6 +113,15 @@ fn parse(input: &str) -> Database {
} }
} }
#[aoc(day5, part1)]
fn part1(input: &Database) -> u64 {
input
.available_ingredients
.iter()
.filter(|i| input.fresh_ingredients.iter().any(|r| r.contains(i)))
.count() as u64
}
#[aoc_generator(day5, part2)] #[aoc_generator(day5, part2)]
fn parse2(input: &str) -> Database2 { fn parse2(input: &str) -> Database2 {
let mut fresh_ingredients = Vec::new(); let mut fresh_ingredients = Vec::new();
@@ -124,22 +135,13 @@ fn parse2(input: &str) -> Database2 {
Database2 { fresh_ingredients } Database2 { fresh_ingredients }
} }
#[aoc(day5, part1)]
fn part1(input: &Database) -> u64 {
input
.available_ingredients
.iter()
.filter(|i| input.fresh_ingredients.iter().any(|r| r.contains(i)))
.count() as u64
}
#[aoc(day5, part2)] #[aoc(day5, part2)]
fn part2(input: &Database2) -> u64 { fn part2(input: &Database2) -> u64 {
let mut all_ingredients = RangeSet::new(); let mut all_ingredients = RangeSet::new();
for r in &input.fresh_ingredients { for r in &input.fresh_ingredients {
all_ingredients.add(r.clone()) all_ingredients.add(r.clone());
} }
all_ingredients.simplify();
all_ingredients.ranges.iter().map(|r| r.len()).sum::<u64>() all_ingredients.ranges.iter().map(|r| r.len()).sum::<u64>()
} }