1
0

Move Cafeteria code into new MultiInterval struct, add FromStr implementation for Interval

This commit is contained in:
Stefan Müller
2026-02-06 00:14:14 +01:00
parent e223c50a0d
commit 5c95df93b7
2 changed files with 104 additions and 55 deletions
+95 -3
View File
@@ -1,12 +1,21 @@
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] use std::collections::BTreeSet;
use std::str::FromStr;
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct Interval { pub struct Interval {
pub start: u64, pub start: u64,
pub end: u64, pub end: u64,
} }
impl Interval { impl Interval {
pub fn new(start: u64, end: u64) -> Interval { const INTERVAL_SPLIT_CHAR: char = '-';
Interval { start, end }
pub fn new(start: u64, end: u64) -> Result<Interval, IntervalError> {
if start <= end {
Ok(Interval { start, end })
} else {
Err(IntervalError::InvalidBoundaries)
}
} }
pub fn contains(&self, value: u64) -> bool { pub fn contains(&self, value: u64) -> bool {
@@ -17,3 +26,86 @@ impl Interval {
self.end - self.start + 1 self.end - self.start + 1
} }
} }
impl FromStr for Interval {
type Err = IntervalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (x_str, y_str) = s
.split_once(Self::INTERVAL_SPLIT_CHAR)
.ok_or(IntervalError::ParseError)?;
let x = x_str
.parse::<u64>()
.map_err(|_| IntervalError::ParseError)?;
let y = y_str
.parse::<u64>()
.map_err(|_| IntervalError::ParseError)?;
Interval::new(x, y)
}
}
pub struct MultiInterval {
intervals: BTreeSet<Interval>,
}
impl MultiInterval {
pub fn new() -> MultiInterval {
MultiInterval {
intervals: BTreeSet::new(),
}
}
pub fn add(&mut self, mut interval: Interval) {
let mut to_delete = Vec::new();
for next in self.intervals.range(&interval..) {
if interval.contains(next.start) || interval.end + 1 == next.start {
// New interval overlaps with the "next" interval already in the collection.
if interval.end < next.end {
interval.end = next.end;
}
to_delete.push(*next);
} else {
break;
}
}
let mut do_insert = true;
if let Some(prev) = self.intervals.range(..=&interval).next_back() {
if interval.contains(prev.end) || prev.end + 1 == interval.start {
// Enlarges new interval to replace the "previous" interval already in the collection.
interval.start = prev.start;
to_delete.push(*prev);
} else if interval.end <= prev.end {
do_insert = false;
}
}
for del in to_delete {
self.intervals.remove(&del);
}
if do_insert {
self.intervals.insert(interval);
}
}
pub fn contains(&self, value: u64) -> bool {
let interval = Interval::new(value, value).unwrap();
if let Some(prev) = self.intervals.range(..=&interval).next_back() {
prev.contains(value)
} else {
false
}
}
pub fn len(&self) -> u64 {
self.intervals.iter().map(|x| x.len()).sum()
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum IntervalError {
ParseError,
InvalidBoundaries,
}
+9 -52
View File
@@ -1,74 +1,31 @@
use crate::common::interval::Interval; use crate::common::interval::MultiInterval;
use crate::solvers::Solver; use crate::solvers::Solver;
use std::collections::BTreeSet;
use std::io::BufRead; use std::io::BufRead;
pub struct Cafeteria {} pub struct Cafeteria {}
impl Cafeteria {
const INTERVAL_SPLIT_CHAR: char = '-';
}
impl Solver for Cafeteria { impl Solver for Cafeteria {
const PUZZLE_INDEX: u8 = 5; const PUZZLE_INDEX: u8 = 5;
const PUZZLE_NAME: &'static str = "Cafeteria"; const PUZZLE_NAME: &'static str = "Cafeteria";
fn process_data<R: BufRead>(&self, mut reader: R) -> (u64, u64) { fn process_data<R: BufRead>(&self, mut reader: R) -> (u64, u64) {
// Builds intervals collection. // Builds intervals collection.
let mut intervals: BTreeSet<Interval> = BTreeSet::new(); let mut intervals = MultiInterval::new();
for line in reader for line in reader
.by_ref() .by_ref()
.lines() .lines()
.map_while(|x| x.ok().filter(|s| !s.is_empty())) .map_while(|x| x.ok().filter(|s| !s.is_empty()))
{ {
// TODO: This code should move into new common::interval::MultiInterval struct. intervals.add(line.parse().unwrap());
let values: Vec<u64> = line
.split(Self::INTERVAL_SPLIT_CHAR)
.map_while(|s| s.parse().ok())
.collect();
let mut interval = Interval::new(values[0], values[1]);
let mut to_delete = Vec::new();
for next in intervals.range(&interval..) {
if interval.contains(next.start) || interval.end + 1 == next.start {
// New interval overlaps with the "next" interval already in the collection.
if interval.end < next.end {
interval.end = next.end;
}
to_delete.push(next.clone());
} else {
break;
}
}
let mut do_insert = true;
if let Some(prev) = intervals.range(..=&interval).next_back() {
if interval.contains(prev.end) || prev.end + 1 == interval.start {
// Enlarges new interval to replace the "previous" interval already in the collection.
interval.start = prev.start;
to_delete.push(prev.clone());
} else if interval.end <= prev.end {
do_insert = false;
}
}
for del in to_delete {
intervals.remove(&del);
}
if do_insert {
intervals.insert(interval);
}
} }
let mut part1 = 0;
// Tests values against intervals. // Tests values against intervals.
for value in reader.lines().map_while(|x| x.ok()?.parse::<u64>().ok()) { let fresh_count = reader
if intervals.iter().any(|interval| interval.contains(value)) { .lines()
part1 += 1; .map_while(|x| x.ok()?.parse::<u64>().ok())
} .filter(|&value| intervals.contains(value))
} .count() as u64;
(part1, intervals.iter().map(|x| x.len()).sum()) (fresh_count, intervals.len())
} }
} }