{-# LANGUAGE TupleSections, ViewPatterns #-}
module Day2 (day2a, day2b) where
import Control.Arrow ((***))
import Data.Bool (bool)
import Data.Function (on)
import Data.List (tails)
import Data.Map.Strict (Map, elems, fromListWith)
import Data.Maybe (catMaybes, isNothing, listToMaybe)
import Data.Monoid (Sum(Sum, getSum), Any(Any, getAny))
count :: (Ord a) => [a] -> Map a Int
count = fromListWith (+) . map (, 1)
day2a :: String -> Int
day2a = uncurry ((*) `on` getSum) . mconcat . map (check . elems . count) . lines
where check = (anySum1 *** anySum1) . mconcat . map (\c -> (Any $ c == 2, Any $ c == 3))
anySum1 = Sum . bool 0 1 . getAny
day2b :: String -> Maybe String
day2b (lines -> input) = listToMaybe
[ catMaybes matches
| one:rest <- tails input
, two <- rest
, let matches = zipWith (\a b -> if a == b then Just a else Nothing) one two
, length (filter isNothing matches) == 1
]