#![warn(clippy::all, clippy::pedantic)] use nom::{character::complete, multi::separated_list1, IResult}; use std::{iter::successors, ops::Not}; /// day 9 part 2 of aoc 2023 /// /// # Arguments /// - input the input for today's puzzle /// /// # Panics /// panics whne it cannot parse the input OR when ever the number of game numbers is greater than /// usize #[must_use] pub fn part2(input: &str) -> String { let (_, report) = parse_input(input).expect("should have valid input for aoc"); report.iter().map(|x| get_next(x)).sum::().to_string() } fn get_next(array: &[i64]) -> i64 { let array = Vec::from(array); let mut a = successors(Some(array.clone()), |a| { a.iter() .all(|x| x == &0) .not() .then_some(a.windows(2).map(|a| a[1] - a[0]).collect::>()) }) .map(|x| *x.first().unwrap()) .collect::>(); a.reverse(); a.iter().fold(0, |acc, x| x - acc) } fn parse_input(input: &str) -> IResult<&str, Vec>> { separated_list1( complete::line_ending, separated_list1(complete::space1, complete::i64), )(input) } #[cfg(test)] mod test { use super::*; use rstest::rstest; #[rstest] #[case(vec![0,3,6,9,12,15], -3)] #[case(vec![1,3,6,10,15,21], 0)] #[case(vec![10,13,16,21,30,45], 5)] fn part2_next(#[case] array: Vec, #[case] expected: i64) { assert_eq!(get_next(&array), expected); } const INPUT: &str = "0 3 6 9 12 15 1 3 6 10 15 21 10 13 16 21 30 45"; #[test] fn part2_works() { let result = part2(INPUT); assert_eq!(result, "2".to_string()); } }