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

<https://adventofcode.com/2017/day/8>

Day 8 poses a problem of parsing a simple programming language,
executing it, and computing simple metrics on the values stored
in variables during execution.

>>> :{
:main +
  "b inc 5 if a > 1\n\
  \a inc 1 if b < 5\n\
  \c dec -10 if a >= 1\n\
  \c inc -20 if c == 10\n"
:}
1
10

-}
module Main where

import Data.Map (Map)
import Data.Map qualified as Map
import Data.Maybe (fromMaybe)

import Advent (format, maximumMaybe, stageTH)

data C = Cinc | Cdec deriving Int -> C -> ShowS
[C] -> ShowS
C -> String
(Int -> C -> ShowS) -> (C -> String) -> ([C] -> ShowS) -> Show C
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> C -> ShowS
showsPrec :: Int -> C -> ShowS
$cshow :: C -> String
show :: C -> String
$cshowList :: [C] -> ShowS
showList :: [C] -> ShowS
Show

stageTH

-- | Compute solution to Day 8. Input file can be overridden with command-line
-- arguments.
--
-- >>> :main
-- 4163
-- 5347
main :: IO ()
IO ()
main =
 do [(String, C, Int, String, String, Int)]
input <- [format|2017 8 (%s @C %d if %s %s %d%n)*|]
    let regmaxes :: [Int]
regmaxes = (Map String Int -> Int) -> [Map String Int] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
0 (Maybe Int -> Int)
-> (Map String Int -> Maybe Int) -> Map String Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map String Int -> Maybe Int
forall (f :: * -> *) a. (Foldable f, Ord a) => f a -> Maybe a
maximumMaybe) ((Map String Int
 -> (String, C, Int, String, String, Int) -> Map String Int)
-> Map String Int
-> [(String, C, Int, String, String, Int)]
-> [Map String Int]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl Map String Int
-> (String, C, Int, String, String, Int) -> Map String Int
interpret Map String Int
forall a. Monoid a => a
mempty [(String, C, Int, String, String, Int)]
input)
    Int -> IO ()
forall a. Show a => a -> IO ()
print ([Int] -> Int
forall a. HasCallStack => [a] -> a
last [Int]
regmaxes)
    Int -> IO ()
forall a. Show a => a -> IO ()
print ([Int] -> Int
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum [Int]
regmaxes)

-- | Given registers and a statement, compute the resulting registers.
interpret ::
  Map String Int {- ^ incoming registers -} ->
  (String, C, Int, String, String, Int) {- ^ statement -} ->
  Map String Int {- ^ outgoing registers -}
interpret :: Map String Int
-> (String, C, Int, String, String, Int) -> Map String Int
interpret Map String Int
regs (String
r1,C
op1,Int
n1,String
r2,String
op2,Int
n2)
  | String -> Int -> Int -> Bool
toCompare String
op2 (Int -> String -> Map String Int -> Int
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault Int
0 String
r2 Map String Int
regs) Int
n2 =
      (Maybe Int -> Maybe Int)
-> String -> Map String Int -> Map String Int
forall k a.
Ord k =>
(Maybe a -> Maybe a) -> k -> Map k a -> Map k a
Map.alter (Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> (Maybe Int -> Int) -> Maybe Int -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. C -> Int -> Int -> Int
toArith C
op1 Int
n1 (Int -> Int) -> (Maybe Int -> Int) -> Maybe Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
0) String
r1 Map String Int
regs
  | Bool
otherwise = Map String Int
regs

-- | Convert the string representation of a comparison to a function.
toCompare ::
  String               {- ^ name     -} ->
  (Int -> Int -> Bool) {- ^ function -}
toCompare :: String -> Int -> Int -> Bool
toCompare String
"<"  = (< )
toCompare String
">"  = (> )
toCompare String
">=" = Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
(>=)
toCompare String
"<=" = Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
(<=)
toCompare String
"!=" = Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
(/=)
toCompare String
"==" = Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
(==)
toCompare String
name = String -> Int -> Int -> Bool
forall a. HasCallStack => String -> a
error (String
"Bad comparison function: " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
name)

-- | Convert the string representation of an arithmetic operation to a function.
toArith ::
  C                   {- ^ name     -} ->
  (Int -> Int -> Int) {- ^ function -}
toArith :: C -> Int -> Int -> Int
toArith C
Cinc = Int -> Int -> Int
forall a. Num a => a -> a -> a
(+)
toArith C
Cdec = Int -> Int -> Int
forall a. Num a => a -> a -> a
subtract