use std::fs::File; use std::io::prelude::*; use std::io::BufReader; use std::time::Instant; use anyhow::Result; const INPUT: &str = "input/input.txt"; fn find_invalid_num(nums: &[usize], preamble: usize) -> Option<usize> { if let Some(num) = nums.windows(preamble + 1).find(|chunk| { for num in &chunk[0..preamble] { for other_num in &chunk[0..preamble] { if num != other_num && num + other_num == chunk[preamble] { return false; } } } true }) { return Some(num[preamble]); } None } fn find_encryption_weakness(nums: &[usize], invalid_num: usize) -> Option<usize> { let mut window_size = 2; while window_size < 1000 { if let Some(weakness) = nums .windows(window_size) .find(|chunk| chunk.iter().sum::<usize>() == invalid_num) { return Some( weakness.iter().min().expect("non-empty slice") + weakness.iter().max().expect("non-empty slice"), ); } else { window_size += 1; } } None } fn solve_part1(input_path: &str, preamble: usize) -> Result<usize> { let file = File::open(input_path)?; let reader = BufReader::new(file); let nums = reader .lines() .map(|line| Ok(line?.parse()?)) .collect::<Result<Vec<usize>>>()?; Ok(find_invalid_num(&nums, preamble).unwrap()) } fn solve_part2(input_path: &str, invalid_num: usize) -> Result<usize> { let file = File::open(input_path)?; let reader = BufReader::new(file); let nums = reader .lines() .map(|line| Ok(line?.parse()?)) .collect::<Result<Vec<usize>>>()?; Ok(find_encryption_weakness(&nums, invalid_num).unwrap()) } fn main() { let mut now = Instant::now(); let part1 = solve_part1(INPUT, 25).unwrap(); println!("Part 1: {}", part1); println!("(elapsed: {:?})", now.elapsed()); now = Instant::now(); println!(""); println!("Part 2: {}", solve_part2(INPUT, part1).unwrap()); println!("(elapsed: {:?})", now.elapsed()); } #[cfg(test)] mod tests { use super::*; const TEST_INPUT: &str = "input/test.txt"; #[test] fn solves_part1() { assert_eq!(solve_part1(TEST_INPUT, 5).unwrap(), 127); } #[test] fn solves_part2() { assert_eq!(solve_part2(TEST_INPUT, 127).unwrap(), 62); } }