{-# LANGUAGE ImportQualifiedPost, QuasiQuotes #-}
{-|
Module      : Main
Description : Day 16 solution
Copyright   : (c) Eric Mertens, 2021
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2016/day/16>

-}
module Main where

import Advent (format)
import Data.Vector.Unboxed (Vector)
import Data.Vector.Unboxed qualified as Vector

toBool :: Char -> Bool
toBool :: Char -> Bool
toBool Char
x = Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'1'

fromBool :: Bool -> Char
fromBool :: Bool -> Char
fromBool Bool
x = if Bool
x then Char
'1' else Char
'0'

part1, part2 :: Int
part1 :: Int
part1 = Int
272
part2 :: Int
part2 = Int
35651584

expand :: Int -> Vector Bool -> Vector Bool
expand :: Int -> Vector Bool -> Vector Bool
expand Int
n Vector Bool
seed
  | Vector Bool -> Int
forall a. Unbox a => Vector a -> Int
Vector.length Vector Bool
seed Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
n = Int -> Vector Bool -> Vector Bool
forall a. Unbox a => Int -> Vector a -> Vector a
Vector.take Int
n Vector Bool
seed
  | Bool
otherwise = Int -> Vector Bool -> Vector Bool
expand Int
n
              (Vector Bool -> Vector Bool) -> Vector Bool -> Vector Bool
forall a b. (a -> b) -> a -> b
$ Vector Bool
seed Vector Bool -> Vector Bool -> Vector Bool
forall a. Semigroup a => a -> a -> a
<> Bool -> Vector Bool
forall a. Unbox a => a -> Vector a
Vector.singleton Bool
False Vector Bool -> Vector Bool -> Vector Bool
forall a. Semigroup a => a -> a -> a
<>
                (Bool -> Bool) -> Vector Bool -> Vector Bool
forall a b. (Unbox a, Unbox b) => (a -> b) -> Vector a -> Vector b
Vector.map Bool -> Bool
not (Vector Bool -> Vector Bool
forall a. Unbox a => Vector a -> Vector a
Vector.reverse Vector Bool
seed)

checksum :: Vector Bool -> [Char]
checksum :: Vector Bool -> [Char]
checksum Vector Bool
v
  | Int -> Bool
forall a. Integral a => a -> Bool
odd Int
n     = Bool -> Char
fromBool (Bool -> Char) -> [Bool] -> [Char]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Vector Bool -> [Bool]
forall a. Unbox a => Vector a -> [a]
Vector.toList Vector Bool
v
  | Bool
otherwise = Vector Bool -> [Char]
checksum
              (Vector Bool -> [Char]) -> Vector Bool -> [Char]
forall a b. (a -> b) -> a -> b
$ Int -> (Int -> Bool) -> Vector Bool
forall a. Unbox a => Int -> (Int -> a) -> Vector a
Vector.generate (Int
nInt -> Int -> Int
forall a. Integral a => a -> a -> a
`quot`Int
2) ((Int -> Bool) -> Vector Bool) -> (Int -> Bool) -> Vector Bool
forall a b. (a -> b) -> a -> b
$ \Int
i ->
                   Vector Bool
v Vector Bool -> Int -> Bool
forall a. Unbox a => Vector a -> Int -> a
Vector.! (Int
2Int -> Int -> Int
forall a. Num a => a -> a -> a
*Int
i) Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Vector Bool
v Vector Bool -> Int -> Bool
forall a. Unbox a => Vector a -> Int -> a
Vector.! (Int
2Int -> Int -> Int
forall a. Num a => a -> a -> a
*Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1)
  where
    n :: Int
n = Vector Bool -> Int
forall a. Unbox a => Vector a -> Int
Vector.length Vector Bool
v

-- | >>> :main
-- 11111000111110000
-- 10111100110110100
main :: IO ()
IO ()
main =
 do [Char]
input <- [format|2016 16 (0|1)*!%n|]
    let v :: Vector Bool
v = [Bool] -> Vector Bool
forall a. Unbox a => [a] -> Vector a
Vector.fromList (Char -> Bool
toBool (Char -> Bool) -> [Char] -> [Bool]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Char]
input)
    [Char] -> IO ()
putStrLn (Vector Bool -> [Char]
checksum (Int -> Vector Bool -> Vector Bool
expand Int
part1 Vector Bool
v))
    [Char] -> IO ()
putStrLn (Vector Bool -> [Char]
checksum (Int -> Vector Bool -> Vector Bool
expand Int
part2 Vector Bool
v))