1use utils::prelude::*;
2
3#[derive(Clone, Debug)]
5pub struct Day06 {
6 part1: u64,
7 part2: u64,
8}
9
10const MAX_NUMBERS: usize = 4;
11const MAX_NUMBER_DIGITS: usize = 4;
12
13impl Day06 {
14 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
15 let mut lines = input.lines().map(str::as_bytes).collect::<Vec<_>>();
16 if lines.len() > MAX_NUMBERS + 1 {
17 return Err(InputError::new(
18 input,
19 0,
20 "expected at most four lines of numbers followed by one line of operators",
21 ));
22 }
23 if lines.len() < 3 {
24 return Err(InputError::new(
25 input,
26 0,
27 "expected at least two lines of numbers followed by one line of operators",
28 ));
29 }
30
31 let operators = lines.pop().unwrap();
32 if let Some(&l) = lines.iter().find(|l| l.len() != operators.len()) {
33 return Err(InputError::new(
34 input,
35 l,
36 "expected all number lines to match operator line length",
37 ));
38 }
39
40 let mut index = 0;
41 let (mut part1, mut part2) = (0, 0);
42 while index < operators.len() {
43 let operator: fn(&[u64]) -> u64 = match operators[index] {
44 b'+' => |x| x.iter().sum(),
45 b'*' => |x| x.iter().product(),
46 _ => return Err(InputError::new(input, operators, "expected '+' or '*'")),
47 };
48
49 let mut column_width = 1;
50 while index + column_width < operators.len() && operators[index + column_width] == b' '
51 {
52 column_width += 1;
53 }
54 let number_width = if index + column_width == operators.len() {
55 column_width
57 } else {
58 column_width - 1
59 };
60 if number_width > MAX_NUMBER_DIGITS {
61 return Err(InputError::new(
62 input,
63 operators,
64 "too many digits in column",
65 ));
66 }
67
68 let mut normal_numbers = [0; MAX_NUMBERS];
69 let mut cephalopod_numbers = [0; MAX_NUMBER_DIGITS];
70 for (i, &l) in lines.iter().enumerate() {
71 let mut start = index;
72 while start < l.len() && l[start] == b' ' {
73 start += 1;
74 }
75 let mut end = index + number_width - 1;
76 while end > start && l[end] == b' ' {
77 end -= 1;
78 }
79
80 let mut normal_number = 0;
81 for i in start..=end {
82 if !matches!(l[i], b'1'..=b'9') {
83 return Err(InputError::new(input, &l[i..], "expected '1'-'9' or ' '"));
84 }
85 normal_number = (normal_number * 10) + (l[i] - b'0') as u64;
86 }
87 normal_numbers[i] = normal_number;
88
89 for (j, &b) in l[index..index + number_width].iter().enumerate() {
90 if b != b' ' {
91 cephalopod_numbers[j] = (cephalopod_numbers[j] * 10) + (b - b'0') as u64;
93 }
94 }
95
96 if index + number_width < l.len() && l[index + number_width] != b' ' {
97 return Err(InputError::new(
98 input,
99 &l[index + number_width..],
100 "expected ' ' between columns",
101 ));
102 }
103 }
104
105 part1 += operator(&normal_numbers[..lines.len()]);
106 part2 += operator(&cephalopod_numbers[..number_width]);
107 index += column_width;
108 }
109
110 Ok(Self { part1, part2 })
111 }
112
113 #[must_use]
114 pub fn part1(&self) -> u64 {
115 self.part1
116 }
117
118 #[must_use]
119 pub fn part2(&self) -> u64 {
120 self.part2
121 }
122}
123
124examples!(Day06 -> (u64, u64) [
125 {
126 input: "123 328 51 64 \n 45 64 387 23 \n 6 98 215 314\n* + * + ",
127 part1: 4277556,
128 part2: 3263827,
129 },
130]);