135 lines
3.2 KiB
Rust
135 lines
3.2 KiB
Rust
use std::fs;
|
|
use std::num::ParseIntError;
|
|
|
|
use itertools::{Itertools, MinMaxResult};
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let input = fs::read_to_string("input")?;
|
|
let xmas = parse_xmas(&input)?;
|
|
|
|
// Part 1
|
|
let result = find_not_summed(&xmas, 25)?;
|
|
println!("{}", result);
|
|
|
|
// Part 2
|
|
let result = find_weakness(&xmas, result)?;
|
|
println!("{}", result);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn parse_xmas(input: &str) -> Result<Vec<u64>, ParseIntError> {
|
|
input.lines().map(|line| line.parse()).collect()
|
|
}
|
|
|
|
fn find_weakness(xmas: &Vec<u64>, key: u64) -> Result<u64, &str> {
|
|
// Now we use the key to find the weakness, by getting all
|
|
// possible sub-sumbs of the list of numbers, and finding the one
|
|
// that happens to match our key
|
|
let weak_window = (2..xmas.len())
|
|
.find_map(|window_size| {
|
|
xmas.windows(window_size)
|
|
.find(|window| window.iter().sum::<u64>() == key)
|
|
})
|
|
.ok_or("Could not find a weakness")?;
|
|
|
|
if let MinMaxResult::MinMax(min, max) = weak_window.iter().minmax() {
|
|
Ok(min + max)
|
|
} else {
|
|
unreachable!("We must have at least two elements due to the window size")
|
|
}
|
|
}
|
|
|
|
fn find_not_summed(xmas: &Vec<u64>, window_size: usize) -> Result<u64, &str> {
|
|
xmas.windows(window_size + 1)
|
|
.find_map(|window| {
|
|
if let [preamble @ .., number] = window {
|
|
// If none of the number combinations in the preamble
|
|
// sum to the final number, we've got the key!
|
|
if !preamble
|
|
.iter()
|
|
.combinations(2)
|
|
.any(|combination| combination.into_iter().sum::<u64>() == *number)
|
|
{
|
|
Some(*number)
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
unreachable!("The windows must all have a length of at least 1");
|
|
}
|
|
})
|
|
.ok_or("Could not find the entry")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use indoc::indoc;
|
|
|
|
#[test]
|
|
fn test_simple() -> Result<(), Box<dyn std::error::Error>> {
|
|
let input = indoc!(
|
|
"35
|
|
20
|
|
15
|
|
25
|
|
47
|
|
40
|
|
62
|
|
55
|
|
65
|
|
95
|
|
102
|
|
117
|
|
150
|
|
182
|
|
127
|
|
219
|
|
299
|
|
277
|
|
309
|
|
576"
|
|
);
|
|
|
|
let xmas = parse_xmas(input)?;
|
|
let result = find_not_summed(&xmas, 5)?;
|
|
|
|
assert_eq!(result, 127);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_simple2() -> Result<(), Box<dyn std::error::Error>> {
|
|
let input = indoc!(
|
|
"35
|
|
20
|
|
15
|
|
25
|
|
47
|
|
40
|
|
62
|
|
55
|
|
65
|
|
95
|
|
102
|
|
117
|
|
150
|
|
182
|
|
127
|
|
219
|
|
299
|
|
277
|
|
309
|
|
576"
|
|
);
|
|
|
|
let xmas = parse_xmas(input)?;
|
|
let key = find_not_summed(&xmas, 5)?;
|
|
let result = find_weakness(&xmas, key)?;
|
|
|
|
assert_eq!(result, 62);
|
|
Ok(())
|
|
}
|
|
}
|