# d24
## Section
```elixir
ExUnit.start()
defmodule D24 do
def parse(input) do
[top, bottom] = String.split(input, "\n\n")
initial =
for line <- String.split(top, "\n"), into: %{} do
[wire, value] = String.split(line, ": ")
{wire,
case value do
"0" -> false
"1" -> true
end}
end
gates =
for line <- String.split(bottom, "\n", trim: true), into: %{} do
[x, opname, y, "->", out] = String.split(line, " ")
op =
case opname do
"AND" -> :&
"OR" -> :|
"XOR" -> :^
end
{out, {op, x, y}}
end
{initial, gates}
end
def calc(op, x, y) do
case op do
:& -> x && y
:| -> x || y
:^ -> x != y
end
end
def step({values, gates}) do
Enum.reduce(gates, {values, %{}}, fn {out, {op, x, y} = gate}, {values, gates} ->
{xval, yval} = {values[x], values[y]}
if is_nil(xval) or is_nil(yval) do
{values, Map.put(gates, out, gate)}
else
{Map.put(values, out, calc(op, xval, yval)), gates}
end
end)
end
def part1(parsed) do
Stream.iterate(parsed, &D24.step/1)
|> Stream.drop_while(fn {_, gates} -> !Enum.empty?(gates) end)
|> Enum.at(0)
|> elem(0)
|> Enum.filter(fn {key, _} -> String.starts_with?(key, "z") end)
|> Enum.sort()
|> Enum.reverse()
|> Enum.map(fn
{_, false} -> "0"
{_, true} -> "1"
end)
|> Enum.join()
|> String.to_integer(2)
end
def norm({op, a, b}), do: if(a < b, do: {op, a, b}, else: {op, b, a})
use ExUnit.Case
test "small" do
input = "x00: 1
x01: 1
x02: 1
y00: 0
y01: 1
y02: 0
x00 AND y00 -> z00
x01 XOR y01 -> z01
x02 OR y02 -> z02
"
assert 4 == parse(input) |> part1()
end
end
ExUnit.run()
```
```elixir
input = File.read!(__DIR__ <> "/input")
D24.parse(input) |> D24.part1()
```
```elixir
import D24
gates = D24.parse(input) |> elem(1)
{:^, "x00", "y00"} = gates["z00"]
{:^, a, b} = gates["z01"] # ghh gnn
{:^, "x01", "y01"} = gates[a]
{:&, "x00", "y00"} = gates[b]
{:^, a, b} = gates["z02"] # msp fwk
{:|, c, d} = gates[a] # nck qhq
{:&, "x01", "y01"} = gates[c]
{:&, e, f} = gates[d] # ghh gnn
# {:^, "x01", "y01"} = gates[e]
# {:&, "x00", "y00"} = gates[f]
{:^, "y02", "x02"} = gates[b]
{:^, a, b} = gates["z03"] # gmp rgn
{:|, c, d} = gates[a] # gpn vbs
{:&, "x02", "y02"} = gates[c]
{:&, e, f} = gates[d] # fwk msp
# {:^, "y02", "x02"} = gates[e]
# {:|, "nck", "qhq"} = gates[f]
{:^, "y03", "x03"} = gates[b]
# 00..45
Map.keys(gates) |> Enum.filter(&String.starts_with?(&1, "z")) |> Enum.sort()
Enum.reduce(2..44, {nil, []}, fn i, {carry, errors} ->
prev = String.pad_leading("#{i - 1}", 2, "0")
num = String.pad_leading("#{i}", 2, "0")
if elem(gates["z#{num}"], 0) == :^ do
{:^, a, b} = gates["z#{num}"]
next_carry = Enum.sort([a, b])
{a, b} = if match?({:^, _, _}, gates[a]), do: {a, b}, else: {b, a}
if elem(gates[a], 0) == :^ do
{:^, c, d} = gates[a]
true = (Enum.sort([c, d]) == ["x#{num}", "y#{num}"])
if elem(gates[b], 0) == :| do
{:|, a, b} = gates[b]
{a, b} = if {:&, "x#{prev}", "y#{prev}"} == norm(gates[a]), do: {a, b}, else: {b, a}
if {:&, "x#{prev}", "y#{prev}"} == norm(gates[a]) do
if elem(gates[b], 0) == :& do
{:&, a, b} = gates[b]
if !is_nil(carry), do: ^carry = Enum.sort([a, b])
{next_carry, errors}
else
{next_carry, [{i, b} | errors]}
end
else
{next_carry, [{i, a} | errors]}
end
else
{next_carry, [{i, b} | errors]}
end
else
{next_carry, [{i, a} | errors]}
end
else
{nil, [{i, "z#{num}"} | errors]}
end
end)
|> elem(1)
|> Enum.map(&elem(&1, 1))
|> Enum.sort()
|> Enum.join(",")
```