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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use utils::prelude::*;

/// Matching palindromes in bracketed sequences.
#[derive(Clone, Debug)]
pub struct Day07 {
    part1: u32,
    part2: u32,
}

impl Day07 {
    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
        // As the input is so large (~175KiB) do everything (validation, part 1, part 2) in 1 pass
        let mut part1_total = 0;
        let mut part2_total = 0;

        for line in input.lines().map(|l| l.as_bytes()) {
            let mut part1_valid = true;
            let mut part1_match = false;

            let mut part2_pairs = [0u8; 26 * 26];
            let mut part2_match = false;

            let mut in_brackets = false;
            for i in 0..line.len() {
                // Validation
                if line[i] == b'[' {
                    if in_brackets {
                        return Err(InputError::new(
                            input,
                            &line[i..],
                            "unexpected nested bracket",
                        ));
                    }
                    in_brackets = true;
                    continue;
                }

                if line[i] == b']' {
                    if !in_brackets {
                        return Err(InputError::new(
                            input,
                            &line[i..],
                            "unexpected close bracket",
                        ));
                    }
                    in_brackets = false;
                    continue;
                }

                if !line[i].is_ascii_lowercase() {
                    return Err(InputError::new(input, &line[i..], "unexpected character"));
                }

                // Shared
                if i + 2 >= line.len() || !line[i + 1].is_ascii_lowercase() {
                    continue; // Can't match either part
                }
                let (a, b, c) = (line[i], line[i + 1], line[i + 2]);

                // Part 1
                if let Some(&d) = line.get(i + 3) {
                    if a != b && a == d && b == c {
                        if in_brackets {
                            part1_valid = false;
                        } else {
                            part1_match = true;
                        }
                    }
                }

                // Part 2
                if a != b && a == c && !part2_match {
                    let index;
                    if in_brackets {
                        index = (b - b'a') as usize * 26 + (a - b'a') as usize; // Reversed
                        part2_pairs[index] |= 1;
                    } else {
                        index = (a - b'a') as usize * 26 + (b - b'a') as usize;
                        part2_pairs[index] |= 2;
                    }
                    part2_match = part2_pairs[index] == 3;
                }
            }

            if in_brackets {
                return Err(InputError::new(
                    input,
                    &line[line.len()..],
                    "expected close bracket",
                ));
            }

            if part1_valid && part1_match {
                part1_total += 1;
            }
            if part2_match {
                part2_total += 1;
            }
        }

        Ok(Self {
            part1: part1_total,
            part2: part2_total,
        })
    }

    #[must_use]
    pub fn part1(&self) -> u32 {
        self.part1
    }

    #[must_use]
    pub fn part2(&self) -> u32 {
        self.part2
    }
}

examples!(Day07 -> (u32, u32) [
    {input: "abba[mnop]qrst", part1: 1},
    {input: "abcd[bddb]xyyx", part1: 0},
    {input: "aaaa[qwer]tyui", part1: 0},
    {input: "ioxxoj[asdfgh]zxcvbn", part1: 1},
    {input: "aba[bab]xyz", part2: 1},
    {input: "xyx[xyx]xyx", part2: 0},
    {input: "aaa[kek]eke", part2: 1},
    {input: "zazbz[bzb]cdb", part2: 1},
]);