Advent of Code 2025 - Day 2

defmodule Day2 do
  def file, do: Parser.read_file(2)
  def test, do: Parser.read_file("test")

  def parse(input) do
    input
    |> Enum.join()
    |> String.split(",")
    |> Enum.map(&(&1 |> String.split("-") |> Enum.map(fn a -> String.to_integer(a) end)))
  end

  def solve(input \\ file()) do
    input
    |> parse()
    |> Enum.reduce([], fn range, acc ->
      range
      |> find_invalid_ids_in_range(&is_symmetric?/1)
      |> Enum.concat(acc)
    end)
    |> Enum.sum()
  end

  def solve_two(input \\ file()) do
    input
    |> parse()
    |> Enum.reduce([], fn range, acc ->
      range
      |> find_invalid_ids_in_range(&contain_pattern?/1)
      |> Enum.concat(acc)
    end)
    |> Enum.sum()
  end

  def find_invalid_ids_in_range([alpha, omega], filter_function) do
    Enum.filter(alpha..omega, &filter_function.(&1))
  end

  def is_symmetric?(number) do
    string = Integer.to_string(number)
    length = String.length(string)
    {a, b} = String.split_at(string, (length / 2) |> trunc())
    a == b
  end

  def contain_pattern?(number) do
    string = Integer.to_string(number)
    length = String.length(string)

    if length == 1 do
      false
    else
      divide_by = Enum.filter(2..length, fn a -> rem(length, a) == 0 end)
      Enum.any?(divide_by, &is_invalid_multiple(string, length, &1))
    end
  end

  def is_invalid_multiple(string, length, divide_by) do
    slice_to = trunc(length / divide_by) - 1
    pattern = string |> String.slice(0..slice_to)
    String.duplicate(pattern, divide_by) == string
  end
end
#!/usr/bin/env elixir

# Advent of Code 2025. Day 2

Mix.install([
  {:nimble_parsec, "~> 1.4.2"},
])

defmodule M do
  import NimbleParsec

  ranges = repeat(
    integer(min: 1)
    |> ignore(string("-"))
    |> integer(min: 1)
    |> tag(:range)
    |> optional(ignore(string(",")))
  )

  defparsec :ranges, ranges

  def invalid1(x) do
    x = to_string(x)
    {a,b} = String.split_at(x, div(String.length(x),2))
    a == b
  end

  def invalid2(x) do
    String.match?(to_string(x), ~r/^(\d+?)\1+$/)
  end
end

ranges = File.read!("../day02.txt")
  |> M.ranges()
  |> then(fn {:ok, lst, "\n", %{}, _, _} -> Enum.map(lst, fn {:range, [lo, hi]} -> (lo..hi) end) end)

# Part 1
ranges
|> Enum.flat_map(fn range -> Enum.filter(range, &M.invalid1/1) end)
|> Enum.sum()
|> IO.inspect(label: "Day 02. Part 1")

# Part 2
ranges
|> Enum.flat_map(fn range -> Enum.filter(range, &M.invalid2/1) end)
|> Enum.sum()
|> IO.inspect(label: "Day 02. Part 2")