diff --git a/src/common/interval.rs b/src/common/interval.rs index 270acf0..0db47ef 100644 --- a/src/common/interval.rs +++ b/src/common/interval.rs @@ -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 start: u64, pub end: u64, } impl Interval { - pub fn new(start: u64, end: u64) -> Interval { - Interval { start, end } + const INTERVAL_SPLIT_CHAR: char = '-'; + + pub fn new(start: u64, end: u64) -> Result { + if start <= end { + Ok(Interval { start, end }) + } else { + Err(IntervalError::InvalidBoundaries) + } } pub fn contains(&self, value: u64) -> bool { @@ -17,3 +26,86 @@ impl Interval { self.end - self.start + 1 } } + +impl FromStr for Interval { + type Err = IntervalError; + + fn from_str(s: &str) -> Result { + let (x_str, y_str) = s + .split_once(Self::INTERVAL_SPLIT_CHAR) + .ok_or(IntervalError::ParseError)?; + let x = x_str + .parse::() + .map_err(|_| IntervalError::ParseError)?; + let y = y_str + .parse::() + .map_err(|_| IntervalError::ParseError)?; + Interval::new(x, y) + } +} + +pub struct MultiInterval { + intervals: BTreeSet, +} + +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, +} diff --git a/src/solvers/cafeteria.rs b/src/solvers/cafeteria.rs index 726de02..2a6baef 100644 --- a/src/solvers/cafeteria.rs +++ b/src/solvers/cafeteria.rs @@ -1,74 +1,31 @@ -use crate::common::interval::Interval; +use crate::common::interval::MultiInterval; use crate::solvers::Solver; -use std::collections::BTreeSet; use std::io::BufRead; pub struct Cafeteria {} -impl Cafeteria { - const INTERVAL_SPLIT_CHAR: char = '-'; -} - impl Solver for Cafeteria { const PUZZLE_INDEX: u8 = 5; const PUZZLE_NAME: &'static str = "Cafeteria"; fn process_data(&self, mut reader: R) -> (u64, u64) { // Builds intervals collection. - let mut intervals: BTreeSet = BTreeSet::new(); + let mut intervals = MultiInterval::new(); for line in reader .by_ref() .lines() .map_while(|x| x.ok().filter(|s| !s.is_empty())) { - // TODO: This code should move into new common::interval::MultiInterval struct. - let values: Vec = 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); - } + intervals.add(line.parse().unwrap()); } - let mut part1 = 0; // Tests values against intervals. - for value in reader.lines().map_while(|x| x.ok()?.parse::().ok()) { - if intervals.iter().any(|interval| interval.contains(value)) { - part1 += 1; - } - } + let fresh_count = reader + .lines() + .map_while(|x| x.ok()?.parse::().ok()) + .filter(|&value| intervals.contains(value)) + .count() as u64; - (part1, intervals.iter().map(|x| x.len()).sum()) + (fresh_count, intervals.len()) } }