123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
use std::{
cmp::Ordering,
fs::File,
io::{BufRead, BufReader},
path::Path, cell::UnsafeCell,
};
macro_rules! path {
($segment:ident $(/ $segments:ident)*) => {{
let mut path = Path::new(stringify!($segment)).to_owned();
$(
path.push(stringify!($segments));
)*
path
}};
}
type CaloriesCount = u32;
#[derive(Default, Clone)]
struct Elf {
items_calories: Vec<CaloriesCount>,
}
struct Elves<Lines: Iterator> {
elf: Option<Elf>,
underlying: Lines,
}
impl<Lines: Iterator<Item = String>> Iterator for Elves<Lines> {
type Item = Elf;
fn next(&mut self) -> Option<Self::Item> {
while let Some(line) = self.underlying.next() {
if line.trim().is_empty() {
if self.elf.is_some() {
return self.elf.take();
}
continue;
}
let calories_count = line.parse::<CaloriesCount>().unwrap_or(0);
if let Some(elf) = &mut self.elf {
elf.items_calories.push(calories_count);
} else {
self.elf = Some(Elf {
items_calories: vec![calories_count],
});
}
}
self.elf.take()
}
}
trait ElvesMethod: Iterator<Item = String>
where
Self: Sized,
{
fn elves(self) -> Elves<Self> {
Elves {
elf: None,
underlying: self,
}
}
}
impl<I: Iterator<Item = String>> ElvesMethod for I {}
#[derive(Clone, Copy)]
enum IntExt<T> {
Finite(T),
MinusInf,
}
impl<T: Ord> PartialEq for IntExt<T> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Finite(lhs), Self::Finite(rhs)) => lhs == rhs,
_ => false,
}
}
}
impl<T: Ord> PartialEq<T> for IntExt<T> {
fn eq(&self, other: &T) -> bool {
match (self, other) {
(Self::Finite(lhs), rhs) => lhs == rhs,
_ => false,
}
}
}
impl<T: Ord> PartialOrd for IntExt<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Self::Finite(lhs), Self::Finite(rhs)) => Some(lhs.cmp(rhs)),
(Self::MinusInf, Self::Finite(_)) => Some(Ordering::Less),
_ => None,
}
}
}
impl<T: Ord + Copy> PartialOrd<T> for IntExt<T> {
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
match (self, other) {
(Self::Finite(lhs), rhs) => Some(lhs.cmp(rhs)),
(lhs, rhs) => lhs.partial_cmp(&Self::Finite(rhs.clone())),
}
}
}
trait TopCaloriesMethod: Iterator<Item = Elf>
where
Self: Sized,
{
fn top_calories<const N: usize>(self) -> [Option<Elf>; N] {
const MINUS_INF: (Option<Elf>, IntExt<CaloriesCount>) = (None, IntExt::MinusInf);
let mut top_elves: [(Option<Elf>, IntExt<CaloriesCount>); N] = [MINUS_INF; N];
for elf in self {
let calories = elf.items_calories.iter().sum::<CaloriesCount>();
for i in 0..N {
if top_elves[i].1 <= calories {
top_elves[i..N].rotate_right(1);
top_elves[i] = (Some(elf), IntExt::Finite(calories));
break;
}
}
}
top_elves.map(|pair| pair.0)
}
}
impl<I: Iterator<Item = Elf>> TopCaloriesMethod for I {}
fn main() {
let input = File::open(path!(path / to / input)).unwrap();
let most_calories = BufReader::new(input)
.lines()
.map(|line| line.unwrap())
.elves()
.top_calories::<3>()
.into_iter()
.map(|elf| {
elf.unwrap_or_default()
.items_calories
.iter()
.sum::<CaloriesCount>()
})
.sum::<CaloriesCount>();
println!("Top three calories counts sum: {}.", most_calories);
}