import Control.Arrow
import Control.Lens
import Data.Function
import Data.List
import Data.List.Split
import Data.Maybe
import Text.Read

type HASH = (String, Maybe Int)

main :: IO ()
main = interact $ show . (day15a &&& day15b . parse) . init

parse :: String -> [HASH]
parse = map (second (readMaybe . tail) . break (`elem` "=-")) . splitOn ","

day15a :: String -> Int
day15a = sum . map (foldl' hashAlg 0) . splitOn ","

day15b :: [HASH] -> Int
day15b =
  sum .
  zipWith (*) [1 ..] .
  map (sum . zipWith (*) [1 ..] . map (fromJust . snd)) .
  foldl' processInstruction (replicate 256 [])

hashAlg :: Int -> Char -> Int
hashAlg x y = (17 * (fromEnum y + x)) `mod` 256

processInstruction :: [[HASH]] -> HASH -> [[HASH]]
processInstruction bs (s, action) = (element box .~ l) bs
  where
    mInsert x = uncurry (++) . second (f x) . break (((==) `on` fst) x)
    f a (_:xs) = a : xs
    f a [] = [a]
    box = foldl' hashAlg 0 s
    newList = mInsert (s, action) (bs !! box)
    newDelList = deleteBy ((==) `on` fst) (s, action) (bs !! box)
    l = maybe newDelList (const newList) action
