defmodule Day05 do def parse_integer_list(list, split) do list |> String.split(split, trim: true) |> Enum.map(&String.to_integer/1) end def parse_data(data) do [rules, jobs] = data |> String.split("\n\n", trim: true) rules = rules |> String.split("\n", trim: true) |> Enum.map(&parse_integer_list(&1, "|")) |> Enum.group_by(&hd/1, &hd(tl(&1))) |> Enum.into(%{}, fn {k, v} -> {k, MapSet.new(v)} end) jobs = jobs |> String.split("\n", trim: true) |> Enum.map(&parse_integer_list(&1, ",")) {rules, jobs} end def part1({rules, jobs}) do jobs |> Enum.filter(&job_okay?(&1, rules)) |> Enum.map(&middle_page/1) |> Enum.sum() end def job_okay?(job, rules) do case job do [] -> true [page | rest] -> page_okay?(page, rest, rules) and job_okay?(rest, rules) end end def page_okay?(page, rest, rules) do rest |> Enum.all?(&Map.get(rules, &1, %MapSet{}) |> MapSet.member?(page) == false) end def middle_page(job) do job |> Enum.at(job |> length() |> div(2)) end def part2({rules, jobs}) do jobs |> Enum.reject(&job_okay?(&1, rules)) |> Enum.map(&fix_job(&1, rules)) |> Enum.map(&middle_page/1) |> Enum.sum() end def fix_job(job, rules, acc \\ []) do case job do [] -> Enum.reverse(acc) job -> {l, r} = Enum.split_with(job, &page_okay?(&1, job, rules) == false) [next, rest] = [hd(r), l ++ tl(r)] fix_job(rest, rules, [next | acc]) end end end data = IO.read(:stdio, :eof) |> Day05.parse_data() {time1 , ans1} = :timer.tc(fn -> Day05.part1(data) end) IO.puts("Time : #{time1 / 1000000}") IO.puts("Answer: #{ans1}") {time2 , ans2} = :timer.tc(fn -> Day05.part2(data) end) IO.puts("Time : #{time2 / 1000000}") IO.puts("Answer: #{ans2}")