import Data.Bits ((.&.), countTrailingZeros, xor) import Data.List.Split (splitOn) import Data.List (foldl', transpose) import qualified Data.Vector.Unboxed as VU import Data.Vector.Unboxed (Vector) isPowTwo :: Int -> Bool isPowTwo n = n .&. (n - 1) == 0 hashChar :: Char -> Int hashChar '#' = 1 hashChar '.' = 0 lineToInt :: String -> Int lineToInt = foldl' (\x y -> 2 * x + hashChar y) 0 matchesAt :: Vector Int -> Int -> Bool matchesAt v i = VU.length differences == 1 && isPowTwo (VU.head differences) where nMirrored = min i $ VU.length v - i leftSide = VU.slice (i - nMirrored) nMirrored v rightSide = VU.slice i nMirrored v differences = VU.filter (/= 0) $ VU.zipWith xor (VU.reverse rightSide) leftSide findMatches :: Vector Int -> [Int] findMatches v = filter (matchesAt v) [1 .. VU.length v - 1] solveDirection :: [String] -> Int solveDirection = last . (0:) . findMatches . VU.fromList . map lineToInt solve :: [String] -> Int solve l = 100 * solveDirection l + (solveDirection . transpose) l main :: IO () main = print . sum . map (solve . lines) . splitOn "\n\n" =<< readFile "data.txt"