From c09fcc61667e921a5ff651fd3127d0f6db578509 Mon Sep 17 00:00:00 2001
From: Sloane Perrault <sloane.perrault@gmail.com>
Date: Wed, 21 Sep 2022 09:19:53 -0400
Subject: [PATCH] solve day 3

---
 2021/README.md     |  3 +-
 2021/lib/2021/3.ex | 89 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 1 deletion(-)
 create mode 100644 2021/lib/2021/3.ex

diff --git a/2021/README.md b/2021/README.md
index c61a9c9..d662ecb 100644
--- a/2021/README.md
+++ b/2021/README.md
@@ -14,7 +14,7 @@
 
 |  S  |  M  |  T  |  W  |  T  |  F  |  S  |
 | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
-|     |     |     | [1] | [2] |  3  |  4  |
+|     |     |     | [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  |
@@ -23,3 +23,4 @@
 
 [1]: ./lib/2021/1.ex
 [2]: ./lib/2021/2.ex
+[3]: ./lib/2021/3.ex
diff --git a/2021/lib/2021/3.ex b/2021/lib/2021/3.ex
new file mode 100644
index 0000000..610abda
--- /dev/null
+++ b/2021/lib/2021/3.ex
@@ -0,0 +1,89 @@
+import AOC
+
+aoc 2021, 3 do
+  def p1 do
+    [first] = Stream.take(input_stream(), 1) |> Enum.to_list()
+
+    bit_length = String.length(first)
+
+    tallies = :atomics.new(bit_length, [])
+
+    Stream.concat([first], input_stream())
+    |> Stream.each(fn value ->
+      for {v, i} <- Enum.with_index(String.split(value, "", trim: true), 1) do
+        :atomics.add(tallies, i, String.to_integer(v))
+      end
+    end)
+    |> Stream.run()
+
+    gamma_str =
+      for b <- 1..bit_length, into: "" do
+        if :atomics.get(tallies, b) > 500 do
+          "1"
+        else
+          "0"
+        end
+      end
+
+    epsilon_str =
+      for b <- String.split(gamma_str, "", trim: true), into: "" do
+        if b == "1", do: "0", else: "1"
+      end
+
+    gamma = String.to_integer(gamma_str, 2)
+    epsilon = String.to_integer(epsilon_str, 2)
+    gamma * epsilon
+  end
+
+  def p2 do
+    input_list =
+      input_stream()
+      |> Enum.map(fn word ->
+        String.split(word, "", trim: true) |> Enum.map(&String.to_integer/1)
+      end)
+
+    word_size = input_list |> hd() |> length()
+
+    [o2_gen_rating] =
+      Enum.reduce(0..word_size, input_list, fn
+        _bit, [value] ->
+          [value]
+
+        bit_index, list ->
+          total_values = length(list)
+
+          bit_sum =
+            for word <- list, reduce: 0 do
+              acc -> acc + Enum.at(word, bit_index)
+            end
+
+          matching_bit = if bit_sum >= total_values / 2, do: 1, else: 0
+
+          Enum.filter(list, fn word -> Enum.at(word, bit_index) == matching_bit end)
+      end)
+
+    o2_gen_rating = Enum.join(o2_gen_rating) |> String.to_integer(2)
+
+    [co2_scrub_rating] =
+      Enum.reduce(0..word_size, input_list, fn
+        _bit, [value] ->
+          [value]
+
+        bit_index, list ->
+          total_values = length(list)
+
+          bit_sum =
+            for word <- list, reduce: 0 do
+              acc -> acc + Enum.at(word, bit_index)
+            end
+
+          matching_bit = if bit_sum >= total_values / 2, do: 0, else: 1
+
+          Enum.filter(list, fn word -> Enum.at(word, bit_index) == matching_bit end)
+      end)
+
+    co2_scrub_rating = Enum.join(co2_scrub_rating) |> String.to_integer(2)
+
+    o2_gen_rating * co2_scrub_rating
+  end
+end