use rayon::prelude::*; use std::io; use std::io::prelude::*; use std::iter; use std::num::ParseIntError; use std::process; use structopt::StructOpt; #[derive(Debug, StructOpt)] #[structopt(name = "Day 16: Flawed Frequency Transmission")] /// Performs the flawed frequency transform of a number. /// /// See https://adventofcode.com/2019/day/16 for details. struct Opt { /// the offset after which you start reading output #[structopt(short = "o", long = "offset", default_value = "0")] offset: usize, input_repeats: usize, fft_repeats: usize, } fn main() { let stdin = io::stdin(); let opt = Opt::from_args(); stdin .lock() .lines() .map(|x| exit_on_failed_assertion(x, "Error reading input")) .map(|x| exit_on_failed_assertion(parse(&x), "Input was not a valid recipe")) .for_each(|input| { println!( "{}", transform(input, opt.input_repeats, opt.fft_repeats, opt.offset) .into_iter() .map(|c| c.to_string()) .collect::() ); }); } fn exit_on_failed_assertion(data: Result, message: &str) -> A { match data { Ok(data) => data, Err(e) => { eprintln!("{}: {}", message, e); process::exit(1); } } } fn parse(s: &str) -> Result, ParseIntError> { s.chars().map(|c| c.to_string().parse::()).collect() } fn transform(input: Vec, input_repeats: usize, fft_repeats: usize, offset: usize) -> Vec { iter::successors( Some( input .iter() .cycle() .take(input.len() * input_repeats) .cloned() .collect::>(), ), |input| Some(next_phase(input, offset)), ) .nth(fft_repeats) .unwrap() .into_iter() .skip(offset) .take(8) .collect() } fn next_phase(input: &Vec, offset: usize) -> Vec { if offset > input.len() / 2 { (0..input.len()) .into_par_iter() .map(|digit| { if digit < offset { 0 } else { input.iter().skip(digit).sum::().abs() % 10 } }) .collect() } else { (0..input.len()) .into_par_iter() .map(|digit| { input .iter() .zip(pattern(digit)) .map(|(x, y)| x * y) .sum::() .abs() % 10 }) .collect() } } fn pattern(digit: usize) -> impl Iterator { iter::repeat(0) .take(digit + 1) .chain(iter::repeat(1).take(digit + 1)) .chain(iter::repeat(0).take(digit + 1)) .chain(iter::repeat(-1).take(digit + 1)) .cycle() .skip(1) }