--- /dev/null
+-- Writeup at https://work.njae.me.uk/2024/12/05/advent-of-code-2024-day-5/
+
+import AoC
+import Data.Text (Text)
+import qualified Data.Text.IO as TIO
+import Data.Attoparsec.Text
+-- import Control.Applicative
+import qualified Data.IntMap.Strict as M
+import Data.IntMap.Strict ((!))
+import qualified Data.Set as S
+import Data.List
+import Data.Maybe
+
+type Page = Int
+type Rules = M.IntMap (S.Set Page)
+
+main :: IO ()
+main =
+ do dataFileName <- getDataFileName
+ text <- TIO.readFile dataFileName
+ let (rulesPairs, books) = successfulParse text
+ let rules = mkRules rulesPairs
+ -- print rules
+ -- print books
+ -- print $ fmap (inValid rules) books
+ print $ part1 rules books
+ print $ part2 rules books
+
+
+part1, part2 :: Rules -> [[Page]] -> Int
+part1 rules books = sum $ fmap middlePage validBooks
+ where validBooks = filter (valid rules) books
+
+part2 rules books = sum $ fmap middlePage reorderedBooks
+ where invalidBooks = filter (not . valid rules) books
+ reorderedBooks = fmap (sortBook rules) invalidBooks
+
+-- a LT b if a must be printed before b
+-- a GT b if a must be printed after b
+-- a EQ b if there is no rule about the order of a and b
+pageOrder :: Rules -> Page -> Page -> Ordering
+pageOrder rules a b
+ | isNothing pa && isNothing pb = EQ
+ | isNothing pa = LT
+ | isNothing pb = GT
+ | S.member a rb = LT
+ | S.member b ra = GT
+ | otherwise = EQ
+ where pa = rules M.!? a
+ pb = rules M.!? b
+ ra = fromJust pa
+ rb = fromJust pb
+
+
+middlePage :: [Page] -> Page
+middlePage b = b !! (length b `div` 2)
+
+sortBook :: Rules -> [Page] -> [Page]
+sortBook rules pages = sortBy (pageOrder rules) pages
+
+-- valid :: Rules -> [Page] -> Bool
+-- valid rules book = sortBook rules book == book
+
+valid :: Rules -> [Page] -> Bool
+valid rules book = fst $ foldr (pageValid rules) (True, []) book
+
+pageValid :: Rules -> Page -> (Bool, [Page]) -> (Bool, [Page])
+pageValid _rules _page (False, pages) = (False, pages)
+pageValid rules page (True, pages) = (allowed, page:pages)
+ where allowed = not $ any mustBeLaterThan pages
+ mustBeLaterThan other = pageOrder rules page other == GT
+
+
+
+mkRules :: [(Page, Page)] -> Rules
+mkRules = foldr go M.empty
+ where go (a, b) m = M.insertWith S.union b (S.singleton a) m
+
+-- parse the input file
+
+rulesBooksP :: Parser ([(Page, Page)], [[Page]])
+rulesP :: Parser [(Page, Page)]
+ruleP :: Parser (Page, Page)
+booksP :: Parser [[Page]]
+bookP :: Parser [Page]
+-- pageP :: Parser Page
+
+rulesBooksP = (,) <$> rulesP <* endOfLine <* endOfLine <*> booksP
+
+rulesP = ruleP `sepBy` endOfLine
+ruleP = (,) <$> decimal <* "|" <*> decimal
+
+booksP = bookP `sepBy` endOfLine
+bookP = decimal `sepBy` ","
+
+successfulParse :: Text -> ([(Page, Page)], [[Page]])
+successfulParse input =
+ case parseOnly rulesBooksP input of
+ Left _err -> ([], []) -- TIO.putStr $ T.pack $ parseErrorPretty err
+ Right rulesBooks -> rulesBooks
+