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

<https://adventofcode.com/2017/day/4>

-}
module Main where

import Advent (countBy, format)
import Data.List (sort, nub)


main :: IO ()
IO ()
main =
  do [[[Char]]]
input <- [format|2017 4 (%s& %n)*|]
     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]] -> Bool
forall a. Ord a => [a] -> Bool
allUnique [[[Char]]]
input)
     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]] -> Bool
forall a. Ord a => [[a]] -> Bool
allUniqueModuloAnagrams [[[Char]]]
input)


-- | Predicate that returns true when all elements in the list are unique
--
-- >>> allUnique (words "aa bb cc dd ee")
-- True
-- >>> allUnique (words "aa bb cc dd aa")
-- False
-- >>> allUnique (words "aa bb cc dd aaa")
-- True
allUnique :: Ord a => [a] -> Bool
allUnique :: forall a. Ord a => [a] -> Bool
allUnique [a]
x = [a]
x [a] -> [a] -> Bool
forall a. Eq a => a -> a -> Bool
== [a] -> [a]
forall a. Eq a => [a] -> [a]
nub [a]
x


-- | Predicate that returns true when all elements in the list are unique
-- when considering anagrams equal to each other.
--
-- >>> allUniqueModuloAnagrams (words "abcde fghij")
-- True
-- >>> allUniqueModuloAnagrams (words "abcde xyz ecdab")
-- False
-- >>> allUniqueModuloAnagrams (words "a ab abc abd abf abj")
-- True
-- >>> allUniqueModuloAnagrams (words "iiii oiii ooii oooi oooo")
-- True
-- >>> allUniqueModuloAnagrams (words "oiii ioii iioi iiio")
-- False
allUniqueModuloAnagrams :: Ord a => [[a]] -> Bool
allUniqueModuloAnagrams :: forall a. Ord a => [[a]] -> Bool
allUniqueModuloAnagrams = [[a]] -> Bool
forall a. Ord a => [a] -> Bool
allUnique ([[a]] -> Bool) -> ([[a]] -> [[a]]) -> [[a]] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> [a]) -> [[a]] -> [[a]]
forall a b. (a -> b) -> [a] -> [b]
map [a] -> [a]
forall a. Ord a => [a] -> [a]
sort