aoc/year2020/
day09.rs

1//! # Custom Customs
2//!
3//! Part one is solved with a brute force search over every possible pair in the preamble, using a
4//! sliding window to advance to each number. To allow testing with the sample data that uses a
5//! preamble of 5 but preserve compile time optimization the `decrypt` method is
6//! [const generic](https://doc.rust-lang.org/reference/items/generics.html#const-generics)
7//! in the size of the preamble.
8//!
9//! Part two uses a sliding search over a variable size window of the input.
10use crate::util::parse::*;
11
12type Result = (u64, u64);
13
14pub fn parse(input: &str) -> Result {
15    decrypt::<25>(input)
16}
17
18pub fn part1(input: &Result) -> u64 {
19    input.0
20}
21
22pub fn part2(input: &Result) -> u64 {
23    input.1
24}
25
26pub fn decrypt<const N: usize>(input: &str) -> Result {
27    let numbers: Vec<_> = input.iter_unsigned().collect();
28
29    let invalid = numbers
30        .windows(N + 1)
31        .find(|w| {
32            for i in 0..(N - 1) {
33                for j in (i + 1)..N {
34                    if w[i] + w[j] == w[N] {
35                        return false;
36                    }
37                }
38            }
39            true
40        })
41        .map(|w| w[N])
42        .unwrap();
43
44    let mut start = 0;
45    let mut end = 2;
46    let mut sum = numbers[0] + numbers[1];
47
48    while sum != invalid {
49        if sum < invalid {
50            sum += numbers[end];
51            end += 1;
52        } else {
53            sum -= numbers[start];
54            start += 1;
55        }
56    }
57
58    let slice = &numbers[start..end];
59    let min = slice.iter().min().unwrap();
60    let max = slice.iter().max().unwrap();
61    let weakness = min + max;
62
63    (invalid, weakness)
64}