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
use utils::parser::ParseError;
use utils::prelude::*;

/// Calculating decompressed length.
#[derive(Clone, Debug)]
pub struct Day09 {
    part1: u64,
    part2: u64,
}

impl Day09 {
    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
        Ok(Self {
            part1: Self::decompressed_length(input.as_bytes(), false).map_with_input(input)?,
            part2: Self::decompressed_length(input.as_bytes(), true).map_with_input(input)?,
        })
    }

    fn decompressed_length(mut input: &[u8], recursive: bool) -> Result<u64, (ParseError, &[u8])> {
        let mut len = 0;

        while !input.is_empty() {
            if input[0] == b'(' {
                let (characters, repeats);
                (characters, input) = parser::u32().parse(&input[1..])?;
                (_, input) = b'x'.parse(input)?;
                (repeats, input) = parser::u32().parse(input)?;
                (_, input) = b')'.parse(input)?;

                if input.len() < characters as usize {
                    return Err((
                        ParseError::Custom("insufficient characters after marker"),
                        input,
                    ));
                }

                let repeated_len = if recursive {
                    Self::decompressed_length(&input[..characters as usize], true)?
                } else {
                    characters as u64
                };

                len += repeated_len * repeats as u64;
                input = &input[characters as usize..];
            } else {
                len += 1;
                input = &input[1..];
            }
        }

        Ok(len)
    }

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

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

examples!(Day09 -> (u64, u64) [
    {input: "ADVENT", part1: 6},
    {input: "A(1x5)BC", part1: 7},
    {input: "(3x3)XYZ", part1: 9, part2: 9},
    {input: "A(2x2)BCD(2x2)EFG", part1: 11},
    {input: "(6x1)(1x3)A", part1: 6},
    {input: "X(8x2)(3x3)ABCY", part1: 18, part2: 20},
    {input: "(27x12)(20x12)(13x14)(7x10)(1x12)A", part2: 241920},
    {input: "(25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN", part2: 445},
]);