1- use std:: { collections:: HashMap , fs, hash:: Hash , ops:: AddAssign } ;
1+ use std:: { collections:: HashMap , fs, hash:: Hash , num :: ParseIntError , ops:: AddAssign , str :: FromStr } ;
22
33pub fn day11 ( input_path : String ) {
44 let content = fs:: read_to_string ( input_path) . unwrap ( ) ;
55
6- let stones: Vec < _ > = content. split_whitespace ( ) . map ( Stone :: parse) . collect ( ) ;
6+ let stones: Vec < _ > = content
7+ . split_whitespace ( )
8+ . map ( |l| l. parse ( ) . unwrap ( ) )
9+ . collect ( ) ;
710
811 println ! ( "{:?}" , part1( stones. iter( ) . cloned( ) . collect( ) ) ) ;
9- println ! ( "{:?}" , part2( stones) ) ;
12+ println ! ( "{:?}" , part2( & stones) ) ;
13+
14+ let stones: Vec < _ > = content
15+ . split_whitespace ( )
16+ . map ( |l| l. parse ( ) . unwrap ( ) ) // this deduces Stone2 because of the signature of part12
17+ . collect ( ) ;
18+
19+ println ! ( "{:?}" , part12( & stones) ) ;
20+ println ! ( "{:?}" , part22( & stones) ) ;
1021}
1122
1223// Most significant digit is last,
@@ -24,12 +35,12 @@ fn part1(stones: Vec<Stone>) -> usize {
2435 . len ( )
2536}
2637
27- fn part2 ( stones : Vec < Stone > ) -> usize {
28- let mut stone_map : HashMap < Stone , usize > =
29- accumulate_into_hashmap ( stones. into_iter ( ) . map ( |s| ( s, 1 ) ) ) ;
38+ fn part2 ( stones : & [ Stone ] ) -> usize {
39+ // order does not matter, so we can compress the stones into a hashmap, and do every operation just once
40+ let mut stone_map : HashMap < Stone , usize > = accumulate ( stones. iter ( ) . map ( |s| ( s. clone ( ) , 1 ) ) ) ;
3041
3142 for _ in 0 ..75 {
32- stone_map = accumulate_into_hashmap (
43+ stone_map = accumulate (
3344 stone_map
3445 . into_iter ( )
3546 . flat_map ( |( stone, count) | blink_at ( stone) . into_iter ( ) . map ( move |s| ( s, count) ) ) ,
@@ -38,20 +49,6 @@ fn part2(stones: Vec<Stone>) -> usize {
3849 stone_map. iter ( ) . map ( |( _, count) | count) . sum ( )
3950}
4051
41- fn accumulate_into_hashmap < T , N , I > ( values : I ) -> HashMap < T , N >
42- where
43- T : Eq + Hash ,
44- N : Default + AddAssign ,
45- I : IntoIterator < Item = ( T , N ) > ,
46- {
47- values
48- . into_iter ( )
49- . fold ( HashMap :: new ( ) , |mut acc, ( key, value) | {
50- * acc. entry ( key) . or_default ( ) += value;
51- acc
52- } )
53- }
54-
5552fn blink_at ( stone : Stone ) -> Vec < Stone > {
5653 match stone {
5754 zero if zero. 0 . is_empty ( ) => vec ! [ Stone :: from( [ 1 ] ) ] ,
@@ -61,17 +58,6 @@ fn blink_at(stone: Stone) -> Vec<Stone> {
6158}
6259
6360impl Stone {
64- // should actually be proper parse
65- fn parse ( string : & str ) -> Stone {
66- Stone :: new (
67- string
68- . chars ( )
69- . map ( |c| c. to_digit ( 10 ) . unwrap ( ) as u8 )
70- . rev ( )
71- . collect ( ) ,
72- )
73- }
74-
7561 fn new ( digits : Vec < u8 > ) -> Stone {
7662 Stone ( digits) . trimmed ( )
7763 }
@@ -108,29 +94,208 @@ impl Stone {
10894 }
10995}
11096
97+ #[ derive( Debug , PartialEq , Eq ) ]
98+ struct ParseStoneError ;
99+
100+ impl FromStr for Stone {
101+ type Err = ParseStoneError ;
102+
103+ // this is just for illustrating
104+ // fn from_str(s: &str) -> Result<Self, Self::Err> {
105+ // let digits: Result<Vec<u8>, ParseStoneError> = s
106+ // .chars()
107+ // .map(|c| c.to_digit(10).map(|d| d as u8).ok_or(ParseStoneError))
108+ // .rev()
109+ // // this collect flips and creates a vector,
110+ // // i.e. it does
111+ // // Iter<Result<u8, Error>> -> Result<Vec<u8>, Error>
112+ // // i.e. it does "early return with Result"
113+ // .collect();
114+ // Ok(Stone::new(digits?))
115+ // }
116+
117+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
118+ s. chars ( )
119+ . map ( |c| c. to_digit ( 10 ) . map ( |d| d as u8 ) . ok_or ( ParseStoneError ) )
120+ . rev ( )
121+ . collect :: < Result < _ , _ > > ( )
122+ . map ( Stone :: new)
123+ }
124+ }
125+
126+ fn accumulate < T , N , I > ( values : I ) -> HashMap < T , N >
127+ where
128+ T : Eq + Hash ,
129+ N : Default + AddAssign ,
130+ I : IntoIterator < Item = ( T , N ) > ,
131+ {
132+ values
133+ . into_iter ( )
134+ . fold ( HashMap :: new ( ) , |mut acc, ( key, value) | {
135+ * acc. entry ( key) . or_default ( ) += value;
136+ acc
137+ } )
138+ }
139+
140+ #[ derive( Debug , PartialEq , Eq , Clone , Hash ) ]
141+ struct Stone2 {
142+ number : usize ,
143+ digits : u32 ,
144+ }
145+
146+ fn digits ( number : usize ) -> u32 {
147+ if number == 0 {
148+ return 1 ;
149+ }
150+ let mut temp = number;
151+ let mut digit_count = 0 ;
152+ while temp > 0 {
153+ temp /= 10 ;
154+ digit_count += 1 ;
155+ }
156+ digit_count
157+ }
158+
159+ impl Stone2 {
160+ fn new ( number : usize ) -> Self {
161+ Stone2 {
162+ number,
163+ digits : digits ( number) ,
164+ }
165+ }
166+
167+ fn split ( self ) -> [ Self ; 2 ] {
168+ // calling this with odd number of digits is ub (I'm lazy)
169+ let divisor = 10usize . pow ( self . digits / 2 ) ;
170+ let left = Stone2 {
171+ number : self . number / divisor,
172+ digits : self . digits / 2 ,
173+ } ;
174+ // right could have leading zeros, recompute digits
175+ let right = Stone2 :: new ( self . number % divisor) ;
176+ [ left, right]
177+ }
178+
179+ fn muled_by ( self , n : usize ) -> Self {
180+ Stone2 :: new ( self . number * n)
181+ }
182+ }
183+
184+ // here, returning impl Iterator<Item = Stone2>
185+ // does not work, because all match arms would need to have the same concrete type
186+ // (which they cannot have, except when we'd implement a bespoke OneOrTwo-struct)
187+ fn blink_at2 ( stone : Stone2 ) -> Vec < Stone2 > {
188+ match stone {
189+ Stone2 {
190+ number : 0 ,
191+ digits : 1 ,
192+ } => vec ! [ Stone2 {
193+ number: 1 ,
194+ digits: 1 ,
195+ } ] ,
196+ even if even. digits % 2 == 0 => Vec :: from ( even. split ( ) ) ,
197+ other => vec ! [ other. muled_by( 2024 ) ] ,
198+ }
199+ }
200+
201+ fn do_rounds ( stones : & [ Stone2 ] , rounds : usize ) -> usize {
202+ let mut stone_map = accumulate ( stones. iter ( ) . map ( |s| ( s. clone ( ) , 1 ) ) ) ;
203+
204+ for _ in 0 ..rounds {
205+ stone_map = accumulate (
206+ stone_map
207+ . into_iter ( )
208+ . flat_map ( |( stone, count) | blink_at2 ( stone) . into_iter ( ) . map ( move |s| ( s, count) ) ) ,
209+ ) ;
210+ }
211+ stone_map. iter ( ) . map ( |( _, count) | count) . sum ( )
212+ }
213+
214+ fn part12 ( stones : & [ Stone2 ] ) -> usize {
215+ do_rounds ( stones, 25 )
216+ }
217+
218+ fn part22 ( stones : & [ Stone2 ] ) -> usize {
219+ do_rounds ( stones, 75 )
220+ }
221+
222+ impl FromStr for Stone2 {
223+ type Err = ParseIntError ;
224+
225+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
226+ s. parse ( ) . map ( |ds| Stone2 :: new ( ds) )
227+ }
228+ }
229+
111230#[ cfg( test) ]
112231mod tests {
113232 use super :: * ;
114233
115234 #[ test]
116235 fn test_muled_by_zero ( ) {
117- let stone = Stone :: parse ( "123" ) ;
236+ let stone: Stone = "123" . parse ( ) . unwrap ( ) ;
118237 let result = stone. muled_by ( 0 ) ;
119238 assert_eq ! ( result. 0 , vec![ 0 , 0 , 0 ] ) ; // 123 * 0 = 0
120239 }
121240
122241 #[ test]
123242 fn test_muled_by_one ( ) {
124- let stone = Stone :: parse ( "456" ) ;
243+ let stone: Stone = "456" . parse ( ) . unwrap ( ) ;
125244 let result = stone. muled_by ( 1 ) ;
126245 assert_eq ! ( result. 0 , vec![ 6 , 5 , 4 ] ) ; // 456 * 1 = 456
127246 }
128247
129248 #[ test]
130249 fn test_muled_by_large_number ( ) {
131- let stone = Stone :: parse ( "999" ) ;
250+ let stone: Stone = "999" . parse ( ) . unwrap ( ) ;
132251 let result = stone. muled_by ( 999 ) ;
133252 // 999 * 999 = 998001
134253 assert_eq ! ( result. 0 , vec![ 1 , 0 , 0 , 8 , 9 , 9 ] ) ;
135254 }
255+
256+ #[ test]
257+ fn test_muled_by_zero2 ( ) {
258+ let stone: Stone2 = "123" . parse ( ) . unwrap ( ) ;
259+ let result = stone. muled_by ( 0 ) ;
260+ assert_eq ! (
261+ result,
262+ Stone2 {
263+ number: 0 ,
264+ digits: 1
265+ }
266+ ) ; // 123 * 0 = 0
267+ }
268+
269+ #[ test]
270+ fn test_muled_by_one2 ( ) {
271+ let stone: Stone2 = "456" . parse ( ) . unwrap ( ) ;
272+ let result = stone. muled_by ( 1 ) ;
273+ assert_eq ! (
274+ result,
275+ Stone2 {
276+ number: 456 ,
277+ digits: 3
278+ }
279+ ) ; // 456 * 1 = 456
280+ }
281+
282+ #[ test]
283+ fn test_muled_by_large_number2 ( ) {
284+ let stone: Stone2 = "999" . parse ( ) . unwrap ( ) ;
285+ let result = stone. muled_by ( 999 ) ;
286+ assert_eq ! (
287+ result,
288+ Stone2 {
289+ number: 998001 ,
290+ digits: 6
291+ }
292+ ) ; // 999 * 999 = 998001
293+ }
294+
295+ #[ test]
296+ fn test_split ( ) {
297+ let stone: Stone2 = "1001" . parse ( ) . unwrap ( ) ;
298+ let result = stone. split ( ) ;
299+ assert_eq ! ( result, [ Stone2 :: new( 10 ) , Stone2 :: new( 1 ) ] ) ;
300+ }
136301}
0 commit comments