1pub mod day1 {
2 fn day1_preprocess(input: &str) -> Vec<i16> {
3 input
4 .lines()
5 .map(|line| {
6 (
7 line.starts_with('L'),
8 line.chars()
9 .skip(1)
10 .collect::<String>()
11 .parse::<i16>()
12 .unwrap(),
13 )
14 })
15 .map(|(is_left, distance)| if is_left { -distance } else { distance })
16 .collect()
17 }
18 pub fn day1_part1(input: &str) -> String {
19 let rotations = day1_preprocess(input);
20 let mut popcnt = 0;
21 let mut pointing_at = 50;
22 for rotation in rotations {
23 pointing_at += rotation;
24 pointing_at %= 100;
25 if pointing_at == 0 {
26 popcnt += 1;
27 }
28 }
29 popcnt.to_string()
30 }
31 pub fn day1_part2(input: &str) -> String {
32 let rotations = day1_preprocess(input);
33 let mut popcnt = 0;
34 let mut pointing_at = 50;
35 for mut rotation in rotations {
36 while rotation != 0 {
37 if rotation < 0 {
38 let distance =
39 rotation.max(-(if pointing_at != 0 { pointing_at } else { 100 }));
40 pointing_at += distance;
41 rotation -= distance;
42 } else if rotation > 0 {
43 let distance = rotation.min(100 - pointing_at);
44 pointing_at += distance;
45 rotation -= distance;
46 }
47 pointing_at %= 100;
48 if pointing_at < 0 {
49 pointing_at += 100;
50 } else if pointing_at == 0 {
51 popcnt += 1;
52 }
53 }
54 }
55 popcnt.to_string()
56 }
57}
58
59pub mod day2 {
60 use std::ops::RangeInclusive;
61
62 pub fn day2_part1(input: &str) -> String {
63 let ranges = parse(input);
64 let invalid = ranges
65 .into_iter()
66 .flatten()
67 .filter(|&id| !is_id_valid(id, true));
68 let invalid_sum: u64 = invalid.sum();
69 invalid_sum.to_string()
70 }
71 pub fn day2_part2(input: &str) -> String {
72 let ranges = parse(input);
73 let invalid = ranges
74 .into_iter()
75 .flatten()
76 .filter(|&id| !is_id_valid(id, false));
77 let invalid_sum: u64 = invalid.sum();
78 invalid_sum.to_string()
79 }
80 fn parse(input: &str) -> Vec<RangeInclusive<u64>> {
81 input
82 .split(',')
83 .map(|range| range.split_once('-').unwrap())
84 .map(|(from, to)| from.parse().unwrap()..=to.parse().unwrap())
85 .collect()
86 }
87 fn is_id_valid(id: u64, max_two_chunks: bool) -> bool {
88 let charnum = id.ilog10() as usize + 1;
89 for chunk_count in 2..=if max_two_chunks { 2 } else { charnum } {
90 if !charnum.is_multiple_of(chunk_count) {
91 continue;
92 }
93 let chunk_size = charnum / chunk_count;
94 let mut equal = true;
95 'chunking: for intra_chunk_index in 0..chunk_size {
96 let first_digit = index(id, intra_chunk_index);
97 for inter_chunk_index in 1..chunk_count {
98 let digit_under_test =
99 index(id, inter_chunk_index * chunk_size + intra_chunk_index);
100 if digit_under_test != first_digit {
101 equal = false;
102 break 'chunking;
103 }
104 }
105 }
106 if equal {
107 return false;
108 }
109 }
110 true
111 }
112 #[inline]
113 fn index(id: u64, index: usize) -> u64 {
114 (id / 10u64.pow(index as u32)) % 10
115 }
116}
117
118pub mod day3;
119pub mod day4;
120pub mod day5;
121pub mod day6;
122pub mod day7;
123pub mod day8;
124pub mod day9;
125mod spatial;
126mod union_find;
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn day9_part1_test() {
134 let test_result = day9::day9_part1(include_str!("../input/day9.test.txt"));
135 assert_eq!(test_result, "50");
136 let result = day9::day9_part1(include_str!("../input/day9.txt"));
137 assert_eq!(result, "4760959496");
138 }
139 #[test]
140 fn day9_part2_test() {
141 let test_result = day9::day9_part2(include_str!("../input/day9.test.txt"));
142 assert_eq!(test_result, "24");
143 let result = day9::day9_part2(include_str!("../input/day9.txt"));
144 assert_eq!(result, "1343576598");
145 }
146
147
148 #[test]
149 fn day8_part1_test() {
150 let test_result = day8::day8_part1(include_str!("../input/day8.test.txt"), 10);
151 assert_eq!(test_result, "40");
152 let result = day8::day8_part1(include_str!("../input/day8.txt"), 1000);
153 assert_eq!(result, "90036");
154 }
155 #[test]
156 fn day8_part2_test() {
157 let test_result = day8::day8_part2(include_str!("../input/day8.test.txt"), 1_000_000);
158 assert_eq!(test_result, "25272");
159 let result = day8::day8_part2(include_str!("../input/day8.txt"), 1_000_000);
160 assert_eq!(result, "6083499488");
161 }
162
163 #[test]
164 fn day7_part1_test() {
165 let test_result = day7::day7_part1(include_str!("../input/day7.test.txt"));
166 assert_eq!(test_result, "21");
167 let result = day7::day7_part1(include_str!("../input/day7.txt"));
168 assert_eq!(result, "1490");
169 }
170 #[test]
171 fn day7_part2_test() {
172 let test_result = day7::day7_part2(include_str!("../input/day7.test.txt"));
173 assert_eq!(test_result, "40");
174 let result = day7::day7_part2(include_str!("../input/day7.txt"));
175 assert_eq!(result, "3806264447357");
176 }
177
178 #[test]
179 fn day6_part1_test() {
180 let test_result = day6::day6_part1(include_str!("../input/day6.test.txt"));
181 assert_eq!(test_result, "4277556");
182 let result = day6::day6_part1(include_str!("../input/day6.txt"));
183 assert_eq!(result, "4951502530386");
184 }
185 #[test]
186 fn day6_part2_test() {
187 let test_result = day6::day6_part2(include_str!("../input/day6.test.txt"));
188 assert_eq!(test_result, "3263827");
189 let result = day6::day6_part2(include_str!("../input/day6.txt"));
190 assert_eq!(result, "8486156119946");
191 }
192
193 #[test]
194 fn day5_part1_test() {
195 let test_result = day5::day5_part1(include_str!("../input/day5.test.txt"));
196 assert_eq!(test_result, "3");
197 let result = day5::day5_part1(include_str!("../input/day5.txt"));
198 assert_eq!(result, "885");
199 }
200 #[test]
201 fn day5_part2_test() {
202 let test_result = day5::day5_part2(include_str!("../input/day5.test.txt"));
203 assert_eq!(test_result, "14");
204 let result = day5::day5_part2(include_str!("../input/day5.txt"));
205 assert_eq!(result, "348115621205535");
206 }
207
208 #[test]
209 fn day4_part1_test() {
210 let test_result = day4::day4_part1(include_str!("../input/day4.test.txt"));
211 assert_eq!(test_result, "13");
212 let result = day4::day4_part1(include_str!("../input/day4.txt"));
213 assert_eq!(result, "1372");
214 }
215 #[test]
216 fn day4_part2_test() {
217 let test_result = day4::day4_part2(include_str!("../input/day4.test.txt"));
218 assert_eq!(test_result, "43");
219 let result = day4::day4_part2(include_str!("../input/day4.txt"));
220 assert_eq!(result, "7922");
221 }
222
223 #[test]
224 fn day3_part1_test() {
225 let test_result = day3::day3_part1(include_str!("../input/day3.test.txt"));
226 assert_eq!(test_result, "357");
227 let result = day3::day3_part1(include_str!("../input/day3.txt"));
228 assert_eq!(result, "17613");
229 }
230 #[test]
231 fn day3_part2_test() {
232 let test_result = day3::day3_part2(include_str!("../input/day3.test.txt"));
233 assert_eq!(test_result, "3121910778619");
234 let result = day3::day3_part2(include_str!("../input/day3.txt"));
235 assert_eq!(result, "175304218462560");
236 }
237
238 #[test]
239 fn day2_part1_test() {
240 let test_result = day2::day2_part1(include_str!("../input/day2.test.txt"));
241 assert_eq!(test_result, "1227775554");
242 let result = day2::day2_part1(include_str!("../input/day2.txt"));
243 assert_eq!(result, "38158151648");
244 }
245 #[test]
246 fn day2_part2_test() {
247 let test_result = day2::day2_part2(include_str!("../input/day2.test.txt"));
248 assert_eq!(test_result, "4174379265");
249 let result = day2::day2_part2(include_str!("../input/day2.txt"));
250 assert_eq!(result, "45283684555");
251 }
252
253 #[test]
254 fn day1_part1_test() {
255 let test_result = day1::day1_part1(include_str!("../input/day1.test.txt"));
256 assert_eq!(test_result, "3");
257 let result = day1::day1_part1(include_str!("../input/day1.txt"));
258 assert_eq!(result, "1021");
259 }
260
261 #[test]
262 fn day1_part2_test() {
263 let test_result = day1::day1_part2(include_str!("../input/day1.test.txt"));
264 assert_eq!(test_result, "6");
265 let result = day1::day1_part2(include_str!("../input/day1.txt"));
266 assert_eq!(result, "5933");
267 }
268}