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, } struct Elves { elf: Option, underlying: Lines, } impl> Iterator for Elves { type Item = Elf; fn next(&mut self) -> Option { 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::().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 where Self: Sized, { fn elves(self) -> Elves { Elves { elf: None, underlying: self, } } } impl> ElvesMethod for I {} #[derive(Clone, Copy)] enum IntExt { Finite(T), MinusInf, } impl PartialEq for IntExt { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::Finite(lhs), Self::Finite(rhs)) => lhs == rhs, _ => false, } } } impl PartialEq for IntExt { fn eq(&self, other: &T) -> bool { match (self, other) { (Self::Finite(lhs), rhs) => lhs == rhs, _ => false, } } } impl PartialOrd for IntExt { fn partial_cmp(&self, other: &Self) -> Option { match (self, other) { (Self::Finite(lhs), Self::Finite(rhs)) => Some(lhs.cmp(rhs)), (Self::MinusInf, Self::Finite(_)) => Some(Ordering::Less), _ => None, } } } impl PartialOrd for IntExt { fn partial_cmp(&self, other: &T) -> Option { match (self, other) { (Self::Finite(lhs), rhs) => Some(lhs.cmp(rhs)), (lhs, rhs) => lhs.partial_cmp(&Self::Finite(rhs.clone())), } } } trait TopCaloriesMethod: Iterator where Self: Sized, { fn top_calories(self) -> [Option; N] { const MINUS_INF: (Option, IntExt) = (None, IntExt::MinusInf); let mut top_elves: [(Option, IntExt); N] = [MINUS_INF; N]; for elf in self { let calories = elf.items_calories.iter().sum::(); 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> 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::() }) .sum::(); println!("Top three calories counts sum: {}.", most_calories); }