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 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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user