1use utils::prelude::*;
2
3#[derive(Clone, Debug)]
5pub struct Day02 {
6 input: Vec<[u64; 2]>,
7}
8
9impl Day02 {
10 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
11 Ok(Self {
12 input: parser::u64()
13 .repeat_n(b'-')
14 .repeat(b',', 1)
15 .parse_complete(input)?,
16 })
17 }
18
19 #[must_use]
20 pub fn part1(&self) -> u64 {
21 self.invalid_id_sum(|repeats| repeats == 2)
22 }
23
24 #[must_use]
25 pub fn part2(&self) -> u64 {
26 self.invalid_id_sum(|repeats| repeats > 1)
27 }
28
29 fn invalid_id_sum(&self, repeat_filter: impl Fn(u32) -> bool) -> u64 {
30 let mut total = 0;
31 let mut patterns = Vec::new();
32
33 for &[start, end] in &self.input {
34 let min_digits = start.checked_ilog10().unwrap_or(0) + 1;
35 let max_digits = end.checked_ilog10().unwrap_or(0) + 1;
36 for repeat_digits in 1..=max_digits / 2 {
37 for digits in (min_digits.next_multiple_of(repeat_digits)..=max_digits)
38 .step_by(repeat_digits as usize)
39 {
40 let repeats = digits / repeat_digits;
41 if !repeat_filter(repeats) {
42 continue;
43 }
44
45 let pow10 = 10u64.pow(repeat_digits);
46
47 let repeat_mask = (0..repeats).fold(0, |acc, _| acc * pow10 + 1);
49
50 let range_start = if digits == min_digits {
52 let x = (start / 10u64.pow(min_digits - repeat_digits)) * repeat_mask;
54 if x < start { x + repeat_mask } else { x }
55 } else {
56 (pow10 / 10) * repeat_mask
57 };
58
59 let range_end = if digits == max_digits {
61 let x = (end / 10u64.pow(max_digits - repeat_digits)) * repeat_mask;
62 x.min(end)
63 } else {
64 (pow10 - 1) * repeat_mask
65 };
66
67 if range_start > range_end || range_end > end {
68 continue;
69 }
70
71 patterns.push((range_start, range_end, repeat_mask));
72 }
73 }
74
75 while patterns.len() > 1 {
77 let min = patterns.iter().map(|&(n, _, _)| n).min().unwrap();
78 total += min;
79
80 patterns.retain_mut(|(n, end, offset)| {
81 if *n == min {
82 *n += *offset;
83 }
84 *n <= *end
85 });
86 }
87
88 if let Some((start, end, step)) = patterns.pop() {
90 let n = ((end - start) / step) + 1;
91 let last = start + (n - 1) * step;
92 total += n * (start + last) / 2;
93 }
94 }
95
96 total
97 }
98}
99
100examples!(Day02 -> (u64, u64) [
101 {
102 input: "11-22,95-115,998-1012,1188511880-1188511890,222220-222224,\
103 1698522-1698528,446443-446449,38593856-38593862,565653-565659,\
104 824824821-824824827,2121212118-2121212124",
105 part1: 1227775554,
106 part2: 4174379265
107 },
108]);