content: -- |
  -- Module      : AOC.Challenge.Day19
  -- Copyright   : (c) Justin Le 2018
  -- License     : BSD3
  --
  -- Maintainer  : justin@jle.im
  -- Stability   : experimental
  -- Portability : non-portable
  --
  -- Day 19.  See "AOC.Solver" for the types used in this module!
  
  module AOC.Challenge.Day19 (
      day19a
    , day19b
    ) where
  
  import           AOC.Common.Elfcode
  import           AOC.Solver                ((:~>)(..))
  import           Control.Lens              (set)
  import           Control.Monad             (mfilter)
  import           Data.Finite               (Finite)
  import qualified Data.Vector.Unboxed.Sized as V
  
  day19a :: (Finite 6, ECProg) :~> Int
  day19a = MkSol
      { sParse = parseElfcode
      , sShow  = show
      , sSolve = \(i, p) -> Just . V.head
                          . execECProg i p
                          $ V.replicate 0
      }
  
  day19b :: (Finite 6, ECProg) :~> Int
  day19b = MkSol
      { sParse = parseElfcode
      , sShow  = show
      , sSolve = \(i, p) -> Just . V.head
                          . execECProg i (optimizeEC [addIfIsFactor i] p)
                          . set (V.ix 0) 1
                          $ V.replicate 0
      }
  
  
  
  addIfIsFactor
      :: Finite 6      -- ^ instruction register
      -> Peephole [Instr]
  addIfIsFactor i = do
      a <- currPeepPos
      let a' = fromIntegral a
      I OSetI _ _ n <- peep (Just 1 ) Nothing   Nothing
      let n' = fromIntegral n
      I OMulR m _ z <- peep Nothing   (Just n') Nothing
      let z' = fromIntegral z
      I OEqRR _ t _ <- peep (Just z') Nothing   (Just z )
      I OAddR _ _ _ <- peep (Just z') (Just i') (Just i )
      I OAddI _ _ _ <- peep (Just i') (Just 1 ) (Just i )
      I OAddR _ o _ <- mfilter (\I{..} -> fromIntegral _iInB == _iOut)
                     $ peep (Just m ) Nothing   Nothing
      I OAddI _ _ _ <- peep (Just n') (Just 1 ) (Just n )
      I OGtRR _ _ _ <- peep (Just n') (Just t ) (Just z )
      I OAddR _ _ _ <- peep (Just i') (Just z') (Just i )
      I OSetI _ _ _ <- peep (Just a') Nothing   (Just i )
      b <- currPeepPos
      let t' = fromIntegral t
          o' = fromIntegral o
      pure . take (b - a) $
          [ I OModR t' m  z           -- store (t `mod` m) to z
          , I OEqRI z' 0  z           -- is z zero?
          , I OAddR z' i' i           -- if yes, jump down
          , I OAddI i' 1  i           -- otherwise, jump down even more
          , I OAddR m  o  o'          -- increment the thing
          ] ++ repeat (I ONoOp 0 0 0)
    where
      i' = fromIntegral i
  