{-# language RecordWildCards #-} import Control.Arrow ((&&&)) import Control.Foldl (Fold(Fold), fold) import Data.Bits (complement, zeroBits, shift, setBit, clearBit, (.|.), (.&.)) import Data.Char (isDigit) import Data.IntMap (IntMap) import qualified Data.IntMap as IM import Data.Maybe (listToMaybe) data Mask = MkMask { clearBits :: Int, setBits :: Int } deriving (Eq, Show) data Write = MkWrite { location :: Int, value :: Int } deriving (Eq, Show) data Instruction = WriteInstr Write | MaskInstr Mask deriving (Eq, Show) readMay :: Read a => String -> Maybe a readMay = fmap fst . listToMaybe . reads parseMask :: String -> Mask parseMask s = MkMask{..} where (clearBits, setBits) = fold ((,) <$> clearFold <*> setFold) s clearFold = Fold updClear (complement zeroBits) id setFold = Fold updSet zeroBits id updClear m c = shiftIn ('0' /= c) m updSet m c = shiftIn ('1' == c) m shiftIn True = flip setBit 0 . flip shift 1 shiftIn False = flip shift 1 parseLine :: String -> Maybe Instruction parseLine ('m':'a':_:_:_:_:_:maskStr) = pure . MaskInstr $ parseMask maskStr parseLine ('m':'e':_:_:memStr) = WriteInstr <$> do location <- readMay locStr value <- readMay valStr return MkWrite{..} where (locStr, rest) = span isDigit memStr valStr = drop 4 rest parseLine _ = Nothing common :: String -> [String] common = lines data ProgramState = MkProgState { mask :: Mask, mem :: IntMap Int } deriving (Eq, Show) initProgState :: ProgramState initProgState = MkProgState { mask = MkMask { clearBits = complement zeroBits, setBits = zeroBits } , mem = IM.empty } setMask :: Mask -> ProgramState -> ProgramState setMask m s = s{ mask = m } writeMem :: Int -> Int -> IntMap Int -> IntMap Int writeMem loc 0 = IM.update (const Nothing) loc writeMem loc val = IM.insert loc val write :: Write -> ProgramState -> ProgramState write MkWrite{..} s = s{ mem = writeMem location (setBits curMask .|. (clearBits curMask .&. value)) (mem s) } where curMask = mask s applyInstruction :: Instruction -> ProgramState -> ProgramState applyInstruction (WriteInstr w) = write w applyInstruction (MaskInstr m) = setMask m runProgram :: [Instruction] -> ProgramState runProgram = fold (Fold (flip applyInstruction) initProgState id) part1 :: [String] -> Maybe Int part1 = fmap (sum . mem . runProgram) . traverse parseLine data MaskBit = Unchanged | Overwritten | Floating deriving (Eq, Show) type Mask2 = [MaskBit] data Instruction2 = WriteInstr2 Write | MaskInstr2 Mask2 deriving (Eq, Show) parseMaskBit :: Char -> Maybe MaskBit parseMaskBit '0' = pure Unchanged parseMaskBit '1' = pure Overwritten parseMaskBit 'X' = pure Floating parseMaskBit _ = Nothing parseMask2 :: String -> Maybe Mask2 parseMask2 = traverse parseMaskBit parseLine2 :: String -> Maybe Instruction2 parseLine2 ('m':'a':_:_:_:_:_:maskStr) = MaskInstr2 <$> parseMask2 maskStr parseLine2 ('m':'e':_:_:memStr) = WriteInstr2 <$> do location <- readMay locStr value <- readMay valStr return MkWrite{..} where (locStr, rest) = span isDigit memStr valStr = drop 4 rest parseLine2 _ = Nothing data ProgramState2 = MkProgState2 { mask2 :: Mask2, mem2 :: IntMap Int } deriving (Eq, Show) initProgState2 :: ProgramState2 initProgState2 = MkProgState2 { mask2 = [] , mem2 = IM.empty } decodeMem :: Mask2 -> Int -> [Int] decodeMem mbs i = snd $ foldr upd (0,[i]) mbs where upd Unchanged (n, is) = (succ n, is) upd Overwritten (n, is) = (succ n, map (flip setBit n) is) upd Floating (n, is) = (succ n, map (flip setBit n) is ++ map (flip clearBit n) is) setMask2 :: Mask2 -> ProgramState2 -> ProgramState2 setMask2 m s = s{ mask2 = m } write2 :: Write -> ProgramState2 -> ProgramState2 write2 MkWrite{..} s = s{ mem2 = foldr (\l -> writeMem l value) (mem2 s) $ decodeMem (mask2 s) location } applyInstruction2 :: Instruction2 -> ProgramState2 -> ProgramState2 applyInstruction2 (WriteInstr2 w) = write2 w applyInstruction2 (MaskInstr2 m) = setMask2 m runProgram2 :: [Instruction2] -> ProgramState2 runProgram2 = fold (Fold (flip applyInstruction2) initProgState2 id) part2 :: [String] -> Maybe Int part2 = fmap (sum . mem2 . runProgram2) . traverse parseLine2 interactive :: Show a => (String -> a) -> IO () interactive f = getContents >>= print . f main :: IO () main = interactive ((part1 &&& part2) . common)