1616//! We then precompute all possible combination for blocks of size 16, using this to accelerate
1717//! part two.
1818use crate :: util:: parse:: * ;
19-
20- const WIDTH : usize = 16 ;
21- const LENGTH : usize = 1 << WIDTH ;
19+ use std:: array:: from_fn;
2220
2321pub fn parse ( input : & str ) -> Vec < i32 > {
2422 input. iter_signed ( ) . collect ( )
@@ -49,37 +47,32 @@ pub fn part2(input: &[i32]) -> usize {
4947 let mut fine = 0 ;
5048 let mut coarse = 0 ;
5149 let mut compact = Vec :: new ( ) ;
52- let mut cache = vec ! [ [ ( 0_u16 , 0_u8 , 0_u8 ) ; LENGTH ] ; WIDTH ] ;
53-
54- // Precompute all possible combinations. For each binary starting number we can start at any
55- // offset from 0..16.
56- for i in 0 ..WIDTH {
57- for j in 0 ..LENGTH {
58- let mut offset = i as u16 ;
59- let mut value = j as u16 ;
60- let mut steps = 0 ;
61-
62- while offset < 16 {
63- value ^= 1 << offset;
64- steps += 1 ;
65- offset += 3 - ( ( value >> offset) & 1 ) ;
66- }
6750
68- cache[ i] [ j] = ( value, steps, offset as u8 - i as u8 ) ;
69- }
70- }
51+ // Precompute all possible combinations for each binary starting number from 0 to 2^16,
52+ // starting at any offset from 0..2.
53+ let cache: Vec < [ _ ; 0x10000 ] > =
54+ ( 0 ..3 ) . map ( |offset| from_fn ( |value| compute_block ( value, offset) ) ) . collect ( ) ;
7155
7256 while index < jump. len ( ) {
7357 if index < coarse {
58+ if index % 16 >= 3 {
59+ let j = index / 16 ;
60+ let ( next, steps, delta) = compute_block ( compact[ j] , index % 16 ) ;
61+
62+ compact[ j] = next as usize ;
63+ total += steps as usize ;
64+ index += delta as usize ;
65+ }
66+
7467 // Index lies within precomputed blocks.
75- let base = index / 16 ;
76- let offset = index % 16 ;
77- let value = compact [ base ] as usize ;
78- let ( next , steps , delta ) = cache [ offset ] [ value ] ;
79-
80- compact [ base ] = next ;
81- total += steps as usize ;
82- index += delta as usize ;
68+ for j in ( index / 16 ) .. ( coarse / 16 ) {
69+ let value = compact [ j ] ;
70+ let ( next , steps , delta ) = cache [ index % 16 ] [ value ] ;
71+
72+ compact [ j ] = next as usize ;
73+ total += steps as usize ;
74+ index += delta as usize ;
75+ }
8376 } else {
8477 // Fall back to part one approach.
8578 let next = index. wrapping_add ( jump[ index] as usize ) ;
@@ -93,7 +86,7 @@ pub fn part2(input: &[i32]) -> usize {
9386 if fine. is_multiple_of ( 16 ) {
9487 let value = ( coarse..fine) . rev ( ) . fold ( 0 , |acc, i| ( acc << 1 ) | ( jump[ i] & 1 ) ) ;
9588 coarse = fine;
96- compact. push ( value as u16 ) ;
89+ compact. push ( value as usize ) ;
9790 }
9891 }
9992
@@ -103,3 +96,17 @@ pub fn part2(input: &[i32]) -> usize {
10396
10497 total
10598}
99+
100+ #[ inline]
101+ fn compute_block ( mut value : usize , mut offset : usize ) -> ( u16 , u8 , u8 ) {
102+ let start = offset;
103+ let mut steps = 0 ;
104+
105+ while offset < 16 {
106+ value ^= 1 << offset;
107+ steps += 1 ;
108+ offset += 3 - ( ( value >> offset) & 1 ) ;
109+ }
110+
111+ ( value as u16 , steps, ( offset - start) as u8 )
112+ }
0 commit comments