defmodule Utils do def parse_integer_list(string, split, trim \\ true) do string |> String.split(split, trim: trim) |> Enum.map(& String.to_integer(&1)) end end defmodule Day07 do defmodule Equation do @derive Inspect defstruct [:result, :terms] def from_string(string) do string |> Utils.parse_integer_list([":", " "]) |> then(& %Equation{result: hd(&1), terms: tl(&1)}) end def lr_solution?(equation, ops) do lr_solution_rev?(equation.result, Enum.reverse(equation.terms), ops) end defp lr_solution_rev?(result, terms, ops) do cond do terms == [] and result == 0 -> true terms == [] or result == 0 -> false true -> ops |> Enum.map(& apply_op_rev(result, hd(terms), &1)) |> Enum.filter(&Kernel.is_integer/1) |> Enum.any?(& lr_solution_rev?(&1, tl(terms), ops)) end end defp apply_op_rev(l, r, :add), do: l >= r and l-r defp apply_op_rev(l, r, :mul), do: rem(l, r) == 0 and div(l, r) defp apply_op_rev(l, r, :concat), do: uncat(l, r) defp uncat(l, 0), do: l defp uncat(0, _), do: false defp uncat(l, r) when rem(l, 10) == rem(r, 10), do: uncat(div(l, 10), div(r, 10)) defp uncat(_, _), do: false end def part1(data) do data |> String.split("\n", trim: true) |> Enum.map(& Equation.from_string(&1)) |> Enum.filter(& Equation.lr_solution?(&1, [:mul, :add])) |> Enum.map(& &1.result) |> Enum.sum() end def part2(data) do data |> String.split("\n", trim: true) |> Enum.map(& Equation.from_string(&1)) |> Enum.filter(& Equation.lr_solution?(&1, [:mul, :concat, :add])) |> Enum.map(& &1.result) |> Enum.sum() end end data = IO.read(:stdio, :eof) {time1 , ans1} = :timer.tc(fn -> Day07.part1(data) end) IO.puts("Time : #{time1 / 1000000}") IO.puts("Answer: #{ans1}") {time2 , ans2} = :timer.tc(fn -> Day07.part2(data) end) IO.puts("Time : #{time2 / 1000000}") IO.puts("Answer: #{ans2}")