Move Cafeteria code into new MultiInterval struct, add FromStr implementation for Interval
This commit is contained in:
+95
-3
@@ -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<Interval, IntervalError> {
|
||||
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<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,
|
||||
}
|
||||
|
||||
@@ -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<R: BufRead>(&self, mut reader: R) -> (u64, u64) {
|
||||
// Builds intervals collection.
|
||||
let mut intervals: BTreeSet<Interval> = 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<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;
|
||||
}
|
||||
intervals.add(line.parse().unwrap());
|
||||
}
|
||||
|
||||
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.
|
||||
for value in reader.lines().map_while(|x| x.ok()?.parse::<u64>().ok()) {
|
||||
if intervals.iter().any(|interval| interval.contains(value)) {
|
||||
part1 += 1;
|
||||
}
|
||||
}
|
||||
let fresh_count = reader
|
||||
.lines()
|
||||
.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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user