This commit is contained in:
Quentin Boyer 2025-12-05 15:15:36 +01:00
parent 05994ef2be
commit fe8a63cd27

202
src/bin/day5.rs Normal file
View file

@ -0,0 +1,202 @@
use std::time::Instant;
use aoc_2025::{load, print_res};
use bstr::BString;
type Parsed = InventoryState;
struct IdRange {
start: u64,
end: u64,
}
impl std::fmt::Debug for IdRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}..={}", self.start, self.end)
}
}
#[derive(Debug)]
pub struct InventoryState {
fresh: Vec<IdRange>,
avail: Vec<u64>,
}
#[inline(never)]
pub fn parsing(input: &BString) -> color_eyre::Result<Parsed> {
let Some((fresh, avail)) = str::from_utf8(input)?.split_once("\n\n") else {
color_eyre::eyre::bail!("No blank line found");
};
Ok(InventoryState {
fresh: fresh
.lines()
.map(|l| {
let Some((start, end)) = l.split_once('-') else {
color_eyre::eyre::bail!("Invalid ID range: {l}");
};
Ok(IdRange {
start: start.parse()?,
end: end.parse()?,
})
})
.collect::<Result<_, _>>()?,
avail: avail
.lines()
.map(|id| id.parse())
.collect::<Result<_, _>>()?,
})
}
#[derive(Debug)]
struct IntervalSet(Vec<IdRange>);
impl IntervalSet {
pub fn new() -> Self {
Self(Vec::new())
}
fn insert_at(&mut self, i: usize, range: IdRange) {
if i == 0 {
if self.0[0].start <= range.end {
todo!()
} else {
self.0.insert(0, range);
}
} else if i == self.0.len() {
if self.0[i - 1].end >= range.start {
todo!()
} else {
self.0.push(range);
}
} else {
// We are in the middle of the array
if self.0[i - 1].end >= range.start {
// The range overlaps with the previous one
if range.end <= self.0[i - 1].end {
// The new range is completly contained in the previous one
} else {
// The range overlaps with at least the previous one
if range.end >= self.0[i].start {
// Part of the range overlaps the next range
let next = self.0.remove(i);
if next.end < range.end {
// The range might overlap with two ranges
todo!()
} else {
// Merge the three ranges together
self.0[i - 1].end = next.end;
}
} else {
// Concatenate this range with the next one
self.0[i - 1].end = range.end;
}
}
} else if self.0[i].start <= range.end {
// The range overlaps with the next one
self.0[i].start = self.0[i].start.min(range.start);
if range.end <= self.0[i].end {
// The range does not go further than the end of the next
} else {
// We have only inserted up to the end of the next range, insert the
// rest
self.insert(IdRange {
start: self.0[i].end + 1,
end: range.end,
})
}
} else {
// We overlap with neither ranges
self.0.insert(i, range)
}
}
}
pub fn insert(&mut self, range: IdRange) {
if self.0.is_empty() {
self.0.push(range);
return;
}
match self.0.binary_search_by_key(&range.start, |r| r.start) {
// Two starts overlap
Ok(i) => {
if range.end <= self.0[i].end {
// Nothing to do, the range is contained inside the existing one
} else {
// Replace the existing range with this new superset
self.0.remove(i);
self.insert(range);
}
}
Err(i) => self.insert_at(i, range),
}
}
pub fn contains(&mut self, value: u64) -> bool {
self.0
.binary_search_by(|r| {
if value < r.start {
std::cmp::Ordering::Greater
} else if value > r.end {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Equal
}
})
.is_ok()
}
pub fn length(&self) -> u64 {
self.0.iter().map(|r| r.end - r.start + 1).sum()
}
}
#[inline(never)]
pub fn part1(input: Parsed) {
let mut ranges = IntervalSet::new();
for fresh in input.fresh.into_iter() {
ranges.insert(fresh);
}
let fresh = input.avail.iter().filter(|&&v| ranges.contains(v)).count();
print_res!("Fresh ingredient count: {fresh}");
}
#[inline(never)]
pub fn part2(input: Parsed) {
let mut ranges = IntervalSet::new();
for fresh in input.fresh.into_iter() {
ranges.insert(fresh);
}
print_res!("Total fresh ingredient count: {}", ranges.length());
}
pub fn main() -> color_eyre::Result<()> {
let context = load()?;
let start = Instant::now();
let parsed = parsing(&context.input)?;
let elapsed = humantime::format_duration(start.elapsed());
let start = Instant::now();
if context.part == 1 {
part1(parsed);
} else {
part2(parsed);
}
let elapsed_part = humantime::format_duration(start.elapsed());
println!(" Parsing: {elapsed}");
println!(" Solving: {elapsed_part}");
Ok(())
}