summaryrefslogtreecommitdiff
path: root/d09/src/main.rs
blob: 7b244871d0170b6ac5309259ca31bfce705baa70 (plain)
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
use std::fs;

fn decompress(input: &str, recurse: bool) -> String {
    let mut output = String::new();

    let mut i = 0;
    let chars: Vec<char> = input.chars().collect();

    while i < chars.len() {
        // If we find a compression marker "(LENGTHxREPETITIONS)"
        if chars[i] == '(' {
            i += 1;
            let mut marker = String::new();
            while chars[i] != ')' {
                marker.push(chars[i]);
                i += 1;
            }
            i += 1; // i -> first char after ')'

            // Read info from the compression marker...
            let parsed: Vec<&str> = marker.split('x').collect();
            let len: usize = parsed[0].parse().unwrap();
            let rep: usize = parsed[1].parse().unwrap();

            // ... and decompress the next `len' characters accordingly
            let replacement = if recurse {
                // Part 2: markers within decompressed data are also handled
                decompress(&input[i..i + len], true)
            } else {
                // Part 1: markers within decompressed data are ignored
                String::from(&input[i..i + len])
            };
            for _n in 0..rep {
                output.push_str(&replacement);
            }

            i += len;
        } else {
            // This part isn't compressed, copy it verbatim
            output.push(chars[i]);
            i += 1;
        }
    }

    output
}

fn main() {
    // Read my initial data from input text file
    let input = fs::read_to_string("input.txt").unwrap();
    let compr = input.trim_end();

    // Part 1 gives 110346 for me
    println!("Part 1 solution: {}", decompress(compr, false).len());

    // Part 2 gives 10774309173 for me. Watch that RAM!
    println!("Part 2 solution: {}", decompress(compr, true).len());
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn part1_example1() {
        let input = "ADVENT";
        assert_eq!(decompress(input, false).len(), 6);
    }

    #[test]
    fn part1_example2() {
        let input = "A(1x5)BC";
        assert_eq!(decompress(input, false).len(), 7);
    }

    #[test]
    fn part1_example3() {
        let input = "(3x3)XYZ";
        assert_eq!(decompress(input, false).len(), 9);
    }

    #[test]
    fn part1_example4() {
        let input = "A(2x2)BCD(2x2)EFG";
        assert_eq!(decompress(input, false).len(), 11);
    }

    #[test]
    fn part1_example5() {
        let input = "(6x1)(1x3)A";
        assert_eq!(decompress(input, false).len(), 6);
    }

    #[test]
    fn part1_example6() {
        let input = "X(8x2)(3x3)ABCY";
        assert_eq!(decompress(input, false).len(), 18);
    }

    #[test]
    fn part2_example1() {
        let input = "(3x3)XYZ";
        assert_eq!(decompress(input, true).len(), 9);
    }

    #[test]
    fn part2_example2() {
        let input = "X(8x2)(3x3)ABCY";
        assert_eq!(decompress(input, true).len(), 20);
    }

    #[test]
    fn part2_example3() {
        let input = "(27x12)(20x12)(13x14)(7x10)(1x12)A";
        assert_eq!(decompress(input, true).len(), 241920);
    }

    #[test]
    fn part2_example4() {
        let input = "(25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN";
        assert_eq!(decompress(input, true).len(), 445);
    }
}