diff --git a/2023/lib/2023/5.ex b/2023/lib/2023/5.ex
index e8aa974..e2774c3 100644
--- a/2023/lib/2023/5.ex
+++ b/2023/lib/2023/5.ex
@@ -1,6 +1,8 @@
 import AOC
 
 aoc 2023, 5 do
+  require Logger
+
   def p1(input) do
     {seeds, maps} = read_input(input)
 
@@ -9,7 +11,47 @@ aoc 2023, 5 do
     |> Enum.min()
   end
 
-  def p2(_input) do
+  def p2(input) do
+    {unparsed_seed_ranges, maps} = read_input(input)
+
+    seed_ranges =
+      unparsed_seed_ranges
+      |> Enum.chunk_every(2)
+      |> Enum.map(fn [start, len] ->
+        start..(start + len)
+      end)
+
+    ranges = simplify_ranges(seed_ranges)
+
+    ranges
+    |> Stream.concat()
+    |> Stream.chunk_every(1_000_000)
+    |> Task.async_stream(
+      fn chunk ->
+        Logger.info("Calculating min for chunk starting at #{inspect(List.first(chunk))}")
+
+        minimum =
+          for i <- chunk, reduce: nil do
+            nil ->
+              traverse_maps(maps, i)
+
+            n ->
+              maps
+              |> traverse_maps(i)
+              |> min(n)
+          end
+
+        Logger.info(
+          "Found minimum for chunk starting at #{inspect(List.first(chunk))}: #{minimum}"
+        )
+
+        minimum
+      end,
+      ordered: false,
+      timeout: :infinity
+    )
+    |> Stream.map(&elem(&1, 1))
+    |> Enum.min()
   end
 
   def traverse_maps(maps, start) do
@@ -23,6 +65,27 @@ aoc 2023, 5 do
     end
   end
 
+  def simplify_ranges([]), do: []
+
+  def simplify_ranges([x | xs]) do
+    case Enum.find(xs, &(not Range.disjoint?(x, &1))) do
+      nil ->
+        [x | simplify_ranges(xs)]
+
+      overlapped_with ->
+        without_overlapped = Enum.reject(xs, &(&1 == overlapped_with))
+
+        o_start..o_end = overlapped_with
+        x_start..x_end = x
+
+        merged = min(o_start, x_start)..max(o_end, x_end)
+
+        simplify_ranges([merged | without_overlapped])
+    end
+  end
+
+  # input parsing
+
   def read_input(input) do
     ["seeds: " <> seeds_str, _ | map_strs] = String.split(input, "\n")