{-# 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 [([[Char]], [[Char]])]
inp <- [format|2020 21 (%s&  %(contains %s&(, )%)%n)*|]
     let [Map [Char] [Char]
soln]   = Map [Char] (Set [Char]) -> [Map [Char] [Char]]
forall (t :: * -> *) a.
(Traversable t, Ord a) =>
t (Set a) -> [t a]
uniqueAssignment ([([[Char]], [[Char]])] -> Map [Char] (Set [Char])
forall a b. (Ord a, Ord b) => [([a], [b])] -> Map b (Set a)
toConstraints [([[Char]], [[Char]])]
inp)
         badFoods :: Set [Char]
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)

     Int -> IO ()
forall a. Show a => a -> IO ()
print (([Char] -> Bool) -> [[Char]] -> Int
forall (f :: * -> *) a. Foldable f => (a -> Bool) -> f a -> Int
countBy ([Char] -> Set [Char] -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.notMember` Set [Char]
badFoods) ((([[Char]], [[Char]]) -> [[Char]])
-> [([[Char]], [[Char]])] -> [[Char]]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ([[Char]], [[Char]]) -> [[Char]]
forall a b. (a, b) -> a
fst [([[Char]], [[Char]])]
inp))
     [Char] -> IO ()
putStrLn ([Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"," (Map [Char] [Char] -> [[Char]]
forall k a. Map k a -> [a]
Map.elems Map [Char] [Char]
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]