1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
use crate::input::Input;

pub fn solve(input: &mut Input) -> Result<u32, String> {
    let mut intervals = Vec::new();
    for line in input.text.lines() {
        let (from, to) = line
            .split_once('-')
            .and_then(|(from, to)| Some((from.parse::<u32>().ok()?, to.parse::<u32>().ok()?)))
            .ok_or("Invalid input")?;
        if from > to {
            return Err("Invalid interval with from > to".into());
        }
        intervals.push((from, to));
    }

    intervals.sort_unstable();

    if input.is_part_one() {
        Ok(intervals.iter().fold(0, |lowest_allowed, &(from, to)| {
            if from > lowest_allowed {
                lowest_allowed
            } else {
                std::cmp::max(lowest_allowed, to + 1)
            }
        }))
    } else {
        let mut in_gaps = intervals[0].0;
        let mut highest_blocked = intervals[0].1;
        for &(from, to) in intervals.iter() {
            if highest_blocked != u32::MAX && from > highest_blocked + 1 {
                in_gaps += from - highest_blocked - 1;
            }
            highest_blocked = std::cmp::max(highest_blocked, to);
        }
        Ok(u32::MAX - highest_blocked + in_gaps)
    }
}

#[test]
pub fn tests() {
    use crate::input::{test_part_one, test_part_two};

    let real_input = include_str!("day20_input.txt");
    test_part_one!(real_input => 17_348_574);
    test_part_two!(real_input => 104);
}