Skip to content

Commit 93a8c80

Browse files
committed
Optimize 2025 day 2
1 parent 9add9a9 commit 93a8c80

File tree

1 file changed

+90
-40
lines changed

1 file changed

+90
-40
lines changed

crates/year2025/src/day02.rs

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,63 +18,113 @@ impl Day02 {
1818

1919
#[must_use]
2020
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 {
2130
let mut total = 0;
31+
let mut streams = Vec::new();
32+
2233
for &[start, end] in &self.input {
23-
'num_loop: for i in start..=end {
24-
let num_digits = i.ilog10() + 1;
34+
let min_digits = start.ilog10() + 1;
35+
let max_digits = end.ilog10() + 1;
36+
for pattern_digits in 1..=max_digits / 2 {
37+
for digits in (min_digits.next_multiple_of(pattern_digits)..=max_digits)
38+
.step_by(pattern_digits as usize)
39+
{
40+
let repeats = digits / pattern_digits;
41+
if !repeat_filter(repeats) {
42+
continue;
43+
}
2544

26-
if num_digits % 2 != 0 {
27-
continue;
45+
let pow10 = 10u64.pow(pattern_digits);
46+
let block = if digits == min_digits {
47+
start / 10u64.pow(min_digits - pattern_digits)
48+
} else {
49+
pow10 / 10
50+
};
51+
52+
streams.push(
53+
RepeatingIdStream {
54+
block,
55+
pow10,
56+
repeats,
57+
range_start: start,
58+
range_end: end,
59+
}
60+
.peekable(),
61+
)
2862
}
63+
}
2964

30-
let pattern_digits = num_digits / 2;
31-
let divisor = 10u64.pow(pattern_digits);
32-
let pattern = i % divisor;
65+
let mut previous_min = None;
66+
loop {
67+
let mut min = None;
3368

34-
let mut remaining = i;
35-
while remaining > 0 {
36-
let chunk = remaining % divisor;
37-
if chunk != pattern {
38-
continue 'num_loop;
69+
// Advance past previous_min (if any), find the next min, and remove any empy streams
70+
streams.retain_mut(|s| {
71+
if s.peek().copied() == previous_min {
72+
let _ = s.next();
3973
}
40-
remaining /= divisor;
41-
}
4274

43-
total += i;
75+
if let Some(&next) = s.peek() {
76+
if min.is_none_or(|n| n > next) {
77+
min = Some(next);
78+
}
79+
true
80+
} else {
81+
false
82+
}
83+
});
84+
85+
let Some(min) = min else {
86+
break;
87+
};
88+
89+
total += min;
90+
previous_min = Some(min);
4491
}
4592
}
93+
4694
total
4795
}
96+
}
4897

49-
#[must_use]
50-
pub fn part2(&self) -> u64 {
51-
let mut total = 0;
52-
for &[start, end] in &self.input {
53-
for i in start..=end {
54-
let num_digits = i.ilog10() + 1;
98+
#[derive(Clone, Debug)]
99+
struct RepeatingIdStream {
100+
block: u64,
101+
pow10: u64,
102+
repeats: u32,
103+
range_start: u64,
104+
range_end: u64,
105+
}
55106

56-
'pattern_loop: for pattern_digits in 1..=num_digits / 2 {
57-
if num_digits % pattern_digits != 0 {
58-
continue;
59-
}
60-
let divisor = 10u64.pow(pattern_digits);
61-
let pattern = i % divisor;
62-
63-
let mut remaining = i;
64-
while remaining > 0 {
65-
let chunk = remaining % divisor;
66-
if chunk != pattern {
67-
continue 'pattern_loop;
68-
}
69-
remaining /= divisor;
70-
}
107+
impl Iterator for RepeatingIdStream {
108+
type Item = u64;
71109

72-
total += i;
73-
break;
74-
}
110+
fn next(&mut self) -> Option<Self::Item> {
111+
while self.block < self.pow10 {
112+
let mut n = 0;
113+
for _ in 0..self.repeats {
114+
n = n * self.pow10 + self.block;
115+
}
116+
self.block += 1;
117+
118+
if n > self.range_end {
119+
// No more solutions in this stream
120+
self.block = self.pow10;
121+
return None;
122+
}
123+
if n >= self.range_start {
124+
return Some(n);
75125
}
76126
}
77-
total
127+
None
78128
}
79129
}
80130

0 commit comments

Comments
 (0)