use std::io::{stdin, BufRead}; use std::iter::*; #[rustfmt::skip] fn read_input() -> Vec { stdin().lock().lines().next().unwrap().unwrap().chars().map(|c| c.to_string().parse().unwrap()).collect() } #[rustfmt::skip] fn part1(mut last_phase: Vec) -> String { for _ in 0..100 { last_phase = (1..=last_phase.len()).map(|i| { let mut pattern = [0i32, 1, 0, -1].iter().flat_map(|x| repeat(x).take(i)).cycle().skip(1); last_phase.iter().map(|x| x*pattern.next().unwrap()).sum::().abs() % 10 }).collect(); } last_phase.iter().take(8).map(|n| n.to_string()).collect::() } /** * The outputs of each phase are essentially a summed-area table, * but built starting with the last element. * This works because – for all elements in the second half of the input vector – * all coefficients before the current index are 0, and all after it are one. * * The examples show this quite clearly: * Input signal: 12345678 (let’s call this `input`) * After 1 phase: 48226158 (`output`) * We can build the state after 1 phase right to left. * ``` * output[7] = input[7..8].iter().sum() % 10; // 8 * output[6] = input[6..8].iter().sum() % 10; // 15 % 10 == 5 * output[5] = input[5..8].iter().sum() % 10; // 21 % 10 == 1 * output[4] = input[4..8].iter().sum() % 10; // 26 % 10 == 6 * ``` * Which is exactly the output sequence. * * The offset is always higher than input.len() / 2, * so this always works. */ #[rustfmt::skip] fn part2(input: Vec) -> String { let offset: usize = input.iter().take(7).map(|n| n.to_string()).collect::().parse().unwrap(); let mut p2 = input.repeat(10_000).split_off(offset); p2.reverse(); for _ in 0..100 { p2 = p2.iter().scan(0, |acc, n| { *acc += n; Some(*acc%10) }).collect(); } p2.iter().rev().take(8).map(|n| n.to_string()).collect::() } fn main() { let input = read_input(); println!("Part 1: {}", part1(input.clone())); println!("Part 2: {}", part2(input)); }