use std::io; use std::io::prelude::*; use std::iter; use std::iter::Sum; use std::num::ParseIntError; use std::ops::Add; use std::str::FromStr; fn main() { let stdin = io::stdin(); let input = stdin .lock() .lines() .map(|l| l.unwrap().parse::().unwrap()); dbg!(fuel_required(input)); } // TODO: If this were a nice CLI program, it would probably have a switch to choose between these two rather than doing both? fn fuel_required(it: impl Iterator) -> (Fuel, Fuel) { it.map(|m| { ( m.fuel_excluding_fuel_weight(), m.fuel_including_fuel_weight(), ) }) .fold((Fuel(0), Fuel(0)), |(sum_x, sum_y), (x, y)| { (sum_x + x, sum_y + y) }) } struct Module { weight: Weight, } impl FromStr for Module { type Err = ParseIntError; fn from_str(s: &str) -> Result { Ok(Module { weight: Weight(s.parse()?), }) } } impl Module { fn fuel_excluding_fuel_weight(&self) -> Fuel { self.weight.required_fuel() } fn fuel_including_fuel_weight(&self) -> Fuel { iter::successors(Some(self.weight.required_fuel()), |fuel| { if fuel.weight().is_zero() { None } else { Some(fuel.weight().required_fuel()) } }) .sum() } } struct Weight(u32); impl Weight { fn is_zero(&self) -> bool { self.0 == 0 } fn required_fuel(&self) -> Fuel { Fuel((self.0 / 3).saturating_sub(2)) } } #[derive(Debug)] struct Fuel(u32); impl Fuel { fn weight(&self) -> Weight { Weight(self.0) } } impl Add for Fuel { type Output = Self; fn add(self, other: Self) -> Self { Fuel(self.0 + other.0) } } impl Sum for Fuel { fn sum>(iter: I) -> Self { iter.fold(Fuel(0), Add::add) } }