@@ -28,106 +28,75 @@ impl Day02 {
2828
2929 fn invalid_id_sum ( & self , repeat_filter : impl Fn ( u32 ) -> bool ) -> u64 {
3030 let mut total = 0 ;
31- let mut streams = Vec :: new ( ) ;
31+ let mut patterns = Vec :: new ( ) ;
3232
3333 for & [ start, end] in & self . input {
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 )
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 )
3939 {
40- let repeats = digits / pattern_digits ;
40+ let repeats = digits / repeat_digits ;
4141 if !repeat_filter ( repeats) {
4242 continue ;
4343 }
4444
45- let pow10 = 10u64 . pow ( pattern_digits) ;
46- let block = if digits == min_digits {
47- start / 10u64 . pow ( min_digits - pattern_digits)
45+ let pow10 = 10u64 . pow ( repeat_digits) ;
46+
47+ // Mask that repeats the pattern (e.g. 1,001,001 for 3x 3 digits)
48+ let repeat_mask = ( 0 ..repeats) . fold ( 0 , |acc, _| acc * pow10 + 1 ) ;
49+
50+ // Smallest number matching the repeated pattern that is >= start
51+ let range_start = if digits == min_digits {
52+ // Repeat the highest N digits of start, + repeat_mask if smaller than the start
53+ let x = ( start / 10u64 . pow ( min_digits - repeat_digits) ) * repeat_mask;
54+ if x < start { x + repeat_mask } else { x }
4855 } else {
49- pow10 / 10
56+ ( pow10 / 10 ) * repeat_mask
5057 } ;
5158
52- streams. push (
53- RepeatingIdStream {
54- block,
55- pow10,
56- repeats,
57- range_start : start,
58- range_end : end,
59- }
60- . peekable ( ) ,
61- )
59+ // Largest number matching the repeated pattern that is <= end
60+ 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) ) ;
6272 }
6373 }
6474
65- let mut previous_min = None ;
66- loop {
67- let mut min = None ;
75+ // Merge and deduplicate multiple sequences
76+ while patterns. len ( ) > 1 {
77+ let min = patterns. iter ( ) . map ( |& ( n, _, _) | n) . min ( ) . unwrap ( ) ;
78+ total += min;
6879
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 ( ) ;
73- }
74-
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
80+ patterns. retain_mut ( |( n, end, offset) | {
81+ if * n == min {
82+ * n += * offset;
8283 }
84+ * n <= * end
8385 } ) ;
86+ }
8487
85- let Some ( min) = min else {
86- break ;
87- } ;
88-
89- total += min;
90- previous_min = Some ( min) ;
88+ // Use the formula for the sum of the arithmetic sequence to compute the final sequence
89+ 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 ;
9193 }
9294 }
9395
9496 total
9597 }
9698}
9799
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- }
106-
107- impl Iterator for RepeatingIdStream {
108- type Item = u64 ;
109-
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) ;
125- }
126- }
127- None
128- }
129- }
130-
131100examples ! ( Day02 -> ( u64 , u64 ) [
132101 {
133102 input: "11-22,95-115,998-1012,1188511880-1188511890,222220-222224,\
0 commit comments