advent_of_code/year2018/
day01.rs1use crate::input::Input;
2use std::collections::HashSet;
3
4type Frequency = i32;
5
6fn parse_frequency_changes(
7 input_string: &str,
8) -> impl Iterator<Item = Result<Frequency, String>> + Clone + '_ {
9 input_string.lines().enumerate().map(|(line_index, line)| {
10 line.parse::<Frequency>()
11 .map_err(|error| format!("Invalid input on line {}: {}", line_index + 1, error))
12 })
13}
14
15pub fn solve(input: &Input) -> Result<Frequency, String> {
16 const MAX_ITERATIONS: usize = 1_000_000;
17 let change_iterator = parse_frequency_changes(input.text);
18
19 if input.is_part_one() {
20 change_iterator.sum::<Result<_, _>>()
21 } else {
22 let mut frequency: Frequency = 0;
23 let mut seen_frequencies = HashSet::from([frequency]);
24
25 let changes: Vec<Frequency> = change_iterator.collect::<Result<_, _>>()?;
26 for &change in changes.iter().cycle().take(MAX_ITERATIONS) {
27 frequency = frequency.checked_add(change).ok_or("Too high frequency")?;
28 if !seen_frequencies.insert(frequency) {
29 return Ok(frequency);
30 }
31 }
32
33 Err(format!(
34 "Frequency not repeated after {MAX_ITERATIONS} iterations"
35 ))
36 }
37}
38
39#[test]
40pub fn tests() {
41 use crate::input::{test_part_one, test_part_two};
42
43 test_part_one!("+1\n-2\n+3\n+1" => 3);
44 test_part_one!("+1\n+1\n+1" => 3);
45 test_part_one!("+1\n+1\n-2" => 0);
46 test_part_one!("-1\n-2\n-3" => -6);
47
48 test_part_two!("+1\n-1" => 0);
49 test_part_two!("+3\n+3\n+4\n-2\n-4" => 10);
50 test_part_two!("-6\n+3\n+8\n+5\n-6" => 5);
51 test_part_two!("+7\n+7\n-2\n-7\n-4" => 14);
52
53 let real_input = include_str!("day01_input.txt");
54 test_part_one!(real_input => 477);
55 test_part_two!(real_input => 390);
56}