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 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 has_lr_solution(equation, ops) do
has_lr_solution(hd(equation.terms), tl(equation.terms), equation.result, ops)
end
defp has_lr_solution(acc, terms, result, ops) do
cond do
acc > result ->
false
terms == [] and acc == result ->
true
terms == [] ->
false
true ->
ops
|> Enum.any?(& has_lr_solution(apply_op(acc, hd(terms), &1), tl(terms), result, ops))
end
end
defp apply_op(l, r, op) do
case op do
:add ->
l + r
:mul ->
l * r
:concat ->
String.to_integer("#{l}#{r}")
end
end
end
defmodule Day07 do
def part1(data) do
data
|> String.split("\n", trim: true)
|> Enum.map(& Equation.from_string(&1))
|> Enum.filter(& Equation.has_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.has_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}")