```import Data.List ``` ``` ``` ``` ``` ```main :: IO () ``` ```main = do ``` ``` input <- readFile "input" ``` ``` putStrLn . formatOutput . both (sum . map length) . groups . map lines . dlfsplit \$ input ``` ``` ``` ``` ``` ```formatOutput :: Show a => (a, a) -> String ``` ```formatOutput (part1,part2) = "Part 1: " ++ show part1 ++ "\nPart 2: " ++ show part2 ``` ``` ``` ```-- Collect each group's answers ``` ```groups :: (Foldable t, Eq a) => [t [a]] -> ([[a]], [[a]]) ``` ```groups [] = ([],[]) ``` ```groups (g:gs) = (foldr1 union g : unions, foldr1 intersect g : intersections) ``` ``` where (unions, intersections) = groups gs ``` ``` ``` ```-- Split a string into a list across all the double newlines in it ``` ```dlfsplit :: String -> [String] ``` ```dlfsplit str = a : case b of ``` ``` [] -> [] ``` ``` b -> dlfsplit b ``` ``` where (a,b) = dlfsplitonce str ``` ``` ``` ```-- Split a string into two across the first double newline found ``` ```dlfsplitonce :: String -> (String, String) ``` ```dlfsplitonce [] = ([], []) ``` ```dlfsplitonce (ca:cb:rest) ``` ``` | ca == '\n' && cb == '\n' = ([], rest) ``` ``` | otherwise = (ca:before, after) ``` ``` where (before, after) = dlfsplitonce (cb : rest) ``` ```dlfsplitonce c = (c, []) ``` ``` ``` ```-- Apply a function to both parts of a pair ``` ```both :: (a -> b) -> (a, a) -> (b, b) ``` ```both f (a,b) = (f a, f b) ``` ``` ```