Day 5
This commit is contained in:
parent
05994ef2be
commit
fe8a63cd27
1 changed files with 202 additions and 0 deletions
202
src/bin/day5.rs
Normal file
202
src/bin/day5.rs
Normal 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(())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue