2023-12-13 09:30:03 +01:00
#![ feature(test, try_blocks) ]
extern crate test ;
use aoc2023 ::{ boilerplate , common ::* } ;
const DAY : usize = 13 ;
2023-12-13 12:08:26 +01:00
type Parsed = Vec < Vec < I > > ;
type I = u32 ;
2023-12-13 09:30:03 +01:00
fn parse_input ( raw : & str ) -> Parsed {
2023-12-13 12:08:26 +01:00
raw . split ( " \n \n " )
. map ( | block | block . lines ( ) . map ( | l | l . bytes ( ) . map ( | b | b = = b '#' ) . fold ( 0 , | acc , n | ( acc < < 1 ) | n as I ) ) . collect ( ) )
. collect ( )
2023-12-13 09:30:03 +01:00
}
2023-12-13 12:08:26 +01:00
fn find_reflection ( block : & [ I ] , blacklist : Option < usize > ) -> Option < usize > {
2023-12-13 09:30:03 +01:00
' outer : for i in 0 .. block . len ( ) - 1 {
let mut offset = 0 ;
loop {
match try { ( block . get ( i . checked_sub ( offset ) ? ) ? , block . get ( i + 1 + offset ) ? ) } {
2023-12-13 10:28:05 +01:00
None if Some ( i ) ! = blacklist = > return Some ( i + 1 ) ,
2023-12-13 09:30:03 +01:00
Some ( ( a , b ) ) if a = = b = > offset + = 1 ,
2023-12-13 10:28:05 +01:00
_ = > continue 'outer ,
2023-12-13 09:30:03 +01:00
}
}
}
None
}
fn part1 ( blocks : & Parsed ) -> usize {
2023-12-13 10:28:05 +01:00
blocks
. iter ( )
. map ( | block | find_reflection ( block , None ) . map ( | n | n * 100 ) . or_else ( | | find_reflection ( & transpose ( block ) , None ) ) . unwrap ( ) )
. sum ( )
2023-12-13 09:30:03 +01:00
}
2023-12-13 12:08:26 +01:00
fn transpose ( orig : & [ I ] ) -> Vec < I > {
let max = orig . iter ( ) . max ( ) . unwrap ( ) ;
let len = ( I ::BITS - max . leading_zeros ( ) ) as usize ;
let mut out = vec! [ 0 ; len ] ;
for j in 0 .. len {
for ( i , & value ) in orig . iter ( ) . enumerate ( ) {
if ( value & ( 1 < < j ) ) ! = 0 {
out [ len - 1 - j ] | = 1 < < ( orig . len ( ) - 1 ) > > i ;
}
2023-12-13 09:30:03 +01:00
}
}
out
}
2023-12-13 10:28:05 +01:00
fn part2 ( blocks : & Parsed ) -> usize {
blocks
. iter ( )
. map ( | block | {
let mut block = block . to_owned ( ) ;
let mut transposed = transpose ( & block ) ;
let p1 = find_reflection ( & block , None ) . map ( | n | n - 1 ) ;
let p1_transposed = find_reflection ( & transposed , None ) . map ( | n | n - 1 ) ;
2023-12-13 12:08:26 +01:00
let max = block . iter ( ) . max ( ) . unwrap ( ) ;
let len = ( I ::BITS - max . leading_zeros ( ) ) as usize ;
2023-12-13 10:28:05 +01:00
for i in 0 .. block . len ( ) {
2023-12-13 12:08:26 +01:00
for j in 0 .. len {
block [ i ] ^ = 1 < < j ;
transposed [ j ] ^ = 1 < < i ;
2023-12-13 10:28:05 +01:00
if let Some ( reflection ) =
find_reflection ( & block , p1 ) . map ( | n | n * 100 ) . or_else ( | | find_reflection ( & transposed , p1_transposed ) )
{
return reflection ;
}
2023-12-13 12:08:26 +01:00
block [ i ] ^ = 1 < < j ;
transposed [ j ] ^ = 1 < < i ;
2023-12-13 10:28:05 +01:00
}
}
unreachable! ( )
} )
. sum ( )
2023-12-13 09:30:03 +01:00
}
boilerplate! {
TEST_INPUT_HORIZONTAL = = " \
#.. . ##.. #
#.. .. #.. #
.. ##.. ###
#####. ##.
#####. ##.
.. ##.. ###
#.. .. #.. #" ,
TEST_INPUT_VERTICAL = = " \
#. ##.. ##.
.. #. ##. #.
##.. .. .. #
##.. .. .. #
.. #. ##. #.
.. ##.. ##.
#. #. ##. #. "
for tests : {
part1 : {
TEST_INPUT_HORIZONTAL = > 400 ,
TEST_INPUT_VERTICAL = > 5 ,
} ,
2023-12-13 10:28:05 +01:00
part2 : {
TEST_INPUT_HORIZONTAL = > 100 ,
TEST_INPUT_VERTICAL = > 300 ,
} ,
2023-12-13 09:30:03 +01:00
} ,
2023-12-13 12:08:26 +01:00
unittests : {
transpose : {
& [
0b100 ,
0b010 ,
] = > TRANSPOSE_42 ,
& [
0b101 ,
0b010 ,
] = > TRANSPOSE_52 ,
} ,
} ,
2023-12-13 09:30:03 +01:00
bench1 = = 37561 ,
2023-12-13 10:28:05 +01:00
bench2 = = 31108 ,
2023-12-13 09:30:03 +01:00
bench_parse : Vec ::len = > 100 ,
}
2023-12-13 12:08:26 +01:00
#[ cfg(test) ]
const TRANSPOSE_52 : & [ I ] = & [ 0b10 , 0b01 , 0b10 ] ;
#[ cfg(test) ]
const TRANSPOSE_42 : & [ I ] = & [ 0b10 , 0b01 , 0b00 ] ;