{-# Language ImportQualifiedPost, QuasiQuotes #-}
{-|
Module      : Main
Description : Day 21 solution
Copyright   : (c) Eric Mertens, 2020
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2020/day/21>

-}
module Main (main) where

import Advent (countBy, uniqueAssignment)
import Advent.Format (format)
import Data.List (intercalate, sort)
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Set (Set)
import Data.Set qualified as Set

-- |
-- >>> :main
-- 2517
-- rhvbn,mmcpg,kjf,fvk,lbmt,jgtb,hcbdb,zrb
main :: IO ()
IO ()
main =
  do inp <- [format|2020 21 (%s&  %(contains %s&(, )%)%n)*|]
     let [soln]   = uniqueAssignment (toConstraints inp)
         badFoods = [[Char]] -> Set [Char]
forall a. Ord a => [a] -> Set a
Set.fromList (Map [Char] [Char] -> [[Char]]
forall k a. Map k a -> [a]
Map.elems Map [Char] [Char]
soln)

     print (countBy (`Set.notMember` badFoods) (concatMap fst inp))
     putStrLn (intercalate "," (Map.elems soln))

toConstraints :: (Ord a, Ord b) => [([a],[b])] -> Map b (Set a)
toConstraints :: forall a b. (Ord a, Ord b) => [([a], [b])] -> Map b (Set a)
toConstraints [([a], [b])]
inp =
  (Set a -> Set a -> Set a) -> [(b, Set a)] -> Map b (Set a)
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith Set a -> Set a -> Set a
forall a. Ord a => Set a -> Set a -> Set a
Set.intersection
    [(b
y, [a] -> Set a
forall a. Ord a => [a] -> Set a
Set.fromList [a]
xs) | ([a]
xs, [b]
ys) <- [([a], [b])]
inp, b
y <- [b]
ys]