summaryrefslogtreecommitdiffstats
path: root/src/day07.exs
blob: 0ec37b6b47b601d772cc3fa6190978522c83a2e3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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}")