use std::{num::ParseIntError, str::FromStr}; use crate::{Problem, Solution}; pub struct Day09; impl Problem for Day09 { const DAY: u8 = 9; const INPUT: &'static str = include_str!("../input/day_09.txt"); } impl Solution for Day09 { type Answer1 = usize; type Answer2 = usize; fn part_1(input: &str) -> anyhow::Result { let data = parse_input(input)?; Ok(data.into_iter().map(extrapolate_forward).sum::() as usize) } fn part_2(input: &str) -> anyhow::Result { let data = parse_input(input)?; Ok(data.into_iter().map(extrapolate_reverse).sum::() as usize) } } fn parse_input(input: &str) -> Result>, ParseIntError> { input.trim().lines().map(parse_line).collect() } fn parse_line(line: &str) -> Result, ParseIntError> { line.split_whitespace().map(FromStr::from_str).collect() } fn extrapolate_forward(mut values: Vec) -> isize { let mut value = 0; while values.iter().any(|n| *n != 0) { value += values.last().copied().unwrap(); values = values.array_windows().map(|[n, m]| m - n).collect(); } value } fn extrapolate_reverse(mut values: Vec) -> isize { let mut tails = Vec::new(); while values.iter().any(|n| *n != 0) { tails.push(values.first().copied().unwrap()); values = values.array_windows().map(|[n, m]| m - n).collect(); } tails.into_iter().rev().fold(0, |acc, n| n - acc) } #[cfg(test)] mod tests { use super::*; const INPUT: &str = indoc::indoc! {" 0 3 6 9 12 15 1 3 6 10 15 21 10 13 16 21 30 45 "}; #[test] fn test_part_1() -> anyhow::Result<()> { Ok(assert_eq!(114, Day09::part_1(INPUT)?)) } #[test] fn test_part_2() -> anyhow::Result<()> { Ok(assert_eq!(2, Day09::part_2(INPUT)?)) } }