Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 3

advent_of_code/2021/day-03.livemd

Day 3

Setup

Mix.install([
  {:kino, "~> 0.5.0"},
  {:nx, "~> 0.1.0"}
])

import Bitwise
input = Kino.Input.textarea("Please paste your input file:")

Part 1

numbers =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&(&1 |> String.to_charlist() |> List.to_tuple()))

[sample | _] = numbers
number_length = tuple_size(sample)
half = div(length(numbers), 2)

gamma_as_list =
  for pos <- 0..(number_length - 1) do
    # We only need to count until half + 1 to know if we have more zeroes than ones
    zero_count = Enum.count_until(numbers, &amp;(elem(&amp;1, pos) == ?0), half + 1)
    if zero_count > half, do: ?0, else: ?1
  end

gamma = List.to_integer(gamma_as_list, 2)
mask = 2 ** number_length - 1
epsilon = bnot(gamma) &amp;&amp;&amp; mask
gamma * epsilon

Part 2

defmodule Recursion do
  defp recur([number], _pos, _fun) do
    number
    |> Tuple.to_list()
    |> List.to_integer(2)
  end

  defp recur(numbers, pos, fun) do
    zero_count = Enum.count(numbers, &amp;(elem(&amp;1, pos) == ?0))
    one_count = length(numbers) - zero_count
    to_keep = fun.(zero_count, one_count)
    numbers = Enum.filter(numbers, &amp;(elem(&amp;1, pos) == to_keep))
    recur(numbers, pos + 1, fun)
  end

  def o2(numbers) do
    recur(numbers, 0, fn zero_count, one_count ->
      if one_count >= zero_count, do: ?1, else: ?0
    end)
  end

  def co2(numbers) do
    recur(numbers, 0, fn zero_count, one_count ->
      if zero_count <= one_count, do: ?0, else: ?1
    end)
  end
end

numbers =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&amp;(&amp;1 |> String.to_charlist() |> List.to_tuple()))

Recursion.o2(numbers) * Recursion.co2(numbers)

Part 1 - Nx

matrix =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(fn line ->
    Enum.map(String.to_charlist(line), &amp;(&amp;1 - ?0))
  end)
  |> Nx.tensor(type: {:u, 8}, names: [:numbers, :digits])

{total, number_length} = Nx.shape(matrix)

ones =
  matrix
  |> Nx.sum(axes: [:numbers])
  |> Nx.greater(div(total, 2))

powers = Nx.reverse(Nx.power(2, Nx.iota({number_length})))
# Nx.power(2, Nx.subtract(number_length - 1, Nx.iota({number_length})))

gamma = Nx.dot(powers, ones) |> Nx.to_scalar()
mask = 2 ** number_length - 1
epsilon = bnot(gamma) &amp;&amp;&amp; mask
gamma * epsilon