{ lib, utils, ... }: with lib; with builtins; with utils; let input_parts = reSplit "\n\n" (readFile ./input); orders = map (reSplit "\\|") (reSplit "\n" (head input_parts)); records = map (reSplit ",") (init (reSplit "\n" (last input_parts))); # defines mapping of number -> [ blocked numbers that can't come after. Sorted for readability. ] blocking = mapAttrs (_: v: map head v |> naturalSort |> unique ) (groupBy last orders); isValid = r: all (i: let p = elemAt r i; in !hasAttr p blocking || (intersectLists (drop (i + 1) r ) blocking."${p}") == [] ) (listRange r); sortRecord = r: if r == [] then [] else (let h = head r; intersect = if !hasAttr h blocking then [] else intersectLists (tail r ) blocking."${h}"; # Idea: check first element, and pull all "bad" elements ahead. If there is no "bad" elements, take the head and go down. in if intersect == [] then [h] ++ sortRecord (tail r) else sortRecord (intersect ++ [ h ] ++ (removeAll intersect (tail r))) ); in { part1 = filter isValid records |> map elemAtMid |> listSumWith toInt; part2 = filter (invert isValid) records |> map sortRecord |> map elemAtMid |> listSumWith toInt; }