Advent of Code 2025 - Day 1

Hello everyone!

This year is going to be shorter, but the difficulty will grow faster. Today I already feel that this is not standard “Day 1” difficulty.

Or I am really overcomplicating things. Didn’t sleep well :smiley:

defmodule AdventOfCode.Solutions.Y25.Day01 do
  alias AoC.Input

  def parse(input, :part_one) do
    input
    |> Input.stream!(trim: true)
    |> Enum.map(fn
      "R" <> n -> {:right, String.to_integer(n)}
      "L" <> n -> {:left, String.to_integer(n)}
    end)
  end

  def parse(input, :part_two) do
    input
    |> Input.stream!(trim: true)
    |> Enum.flat_map(fn
      "R" <> n -> expand_rotation(:right, String.to_integer(n))
      "L" <> n -> expand_rotation(:left, String.to_integer(n))
    end)
  end

  defp expand_rotation(direction, amount) when amount <= 100 do
    [{direction, amount}]
  end

  defp expand_rotation(direction, amount) when amount > 100 do
    [{direction, 100} | expand_rotation(direction, amount - 100)]
  end

  def part_one(problem) do
    problem
    |> Stream.scan(50, fn
      {:left, n}, acc -> Integer.mod(acc - n, 100)
      {:right, n}, acc -> Integer.mod(acc + n, 100)
    end)
    |> Enum.count(&(&1 == 0))
  end

  def part_two(problem) do
    problem
    |> Stream.transform(50, fn
      {:left, n}, acc ->
        new_acc = Integer.mod(acc - n, 100)
        {[{:left, acc, new_acc}], new_acc}

      {:right, n}, acc ->
        new_acc = Integer.mod(acc + n, 100)
        {[{:right, acc, new_acc}], new_acc}
    end)
    |> Enum.count(fn
      {_, _, 0} -> true
      {_, 0, _} -> false
      {:right, a, b} when a > b -> true
      {:left, a, b} when a < b -> true
      {_, same, same} -> true
      _ -> false
    end)
  end
end

6 Likes

This year I am using Clojure. But I did something similar. Had to look for all the cases:

  1. Get the zero passed by div value and 100
  2. Is the number already starting at 0 and current diff is negative? The nothing changes, otherwise, incr the zero values
  3. Is the difference after rotation more than 100? Then we definitely crossed zero one more time - incr the zero values
  4. Is the diff zero? Yup, increment again!

That was how I did the second part. One interesting thing, if you miss 2 or 4 - the example inputs give you 6 - but the final input doesn’t match, I wonder if that was intentional.

Another thing, that reminds me of the sevenine situation in 2023 - the > 100 cases was hidden from example input. Sneaky lol.

I have a feeling I can get a formula for this. Will be following y’all to see how you did it.

3 Likes

Yes, definitely a little bit harder, especially part 2.

defmodule Day01 do
  def part1(input) do
    parse(input)
    |> Enum.scan(50, &(Integer.mod(&1 + &2, 100)))
    |> Enum.count(&(&1 === 0))
  end

  def part2(input) do
    parse(input)
    |> Enum.reduce({50, 0}, fn amount, {dial, crossings} ->
      new_dial = dial + rem(amount, 100)

      crossings = crossings + div(abs(amount), 100)

      crossings = cond do
        dial === 0 ->
          crossings
        new_dial === 0 or new_dial === 100 ->
          crossings
        new_dial in 1..99 ->
          crossings
        true ->
          crossings + 1
      end

      new_dial = Integer.mod(new_dial, 100)
      crossings = if new_dial === 0 do
        crossings + 1
      else
        crossings
      end
      {new_dial, crossings}
    end)
    |> then(&elem(&1, 1))
  end

  defp parse(input) do
    input
    |> Enum.map(fn line ->
      case line do
        "L" <> int -> -String.to_integer(int)
        "R" <> int -> String.to_integer(int)
      end
    end)
  end
end

2 Likes

oh, so many off-by-one errors in part 2. I’m a wee bit out of practice!

My insight was that wrapping from 0 → 99 → 00 doesn’t actually matter - all that matters is whether or not the current position is divisible by 100.

1 Like

Part 2 was PITA, but managed:

instructions =
  puzzle_input
  |> String.split("\n", trim: true)
  |> Enum.map(fn
    <<dir>> <> rest ->
      num = String.to_integer(rest)
      if dir == ?R do
        {div(num, 100), rem(num, 100)}
      else
        {div(num, 100), -rem(num, 100)}
      end
  end)

## Part 1

Enum.reduce(instructions, {50, 0}, fn {_rot, val}, {curr, sum} ->
  next = Integer.mod(curr + val, 100)

  {next, sum + if(next == 0, do: 1, else: 0)}
end)
|> elem(1)

## Part 2

Enum.reduce(instructions, {50, 0}, fn {rot, val}, {curr, sum} ->
  next = curr + val
  pass =
    cond do
      curr == 0 and next < 0 -> 0
      next not in 0..99 -> 1
      rem(next, 100) == 0 -> 1
      true -> 0
    end

  {Integer.mod(next, 100), sum + pass + rot}
end)
|> elem(1)
1 Like

Not too hard, had to wrap my head around how many zero crossing occur when turning left.

defmodule RAoc.Solutions.Y25.Day01 do
  alias AoC.Input

  def parse(input, _part) do
    Input.stream!(input, trim: true)
    |> Enum.to_list()
  end

  def part_one(problem) do
    state = run(problem)
    state.zeroes
  end

  defp run(turns) do
    state = %{dial: 50, zeroes: 0, any_zero: 0}

    Enum.reduce(turns, state, fn <<dir::binary-size(1), sdist::binary>>,
                                 %{dial: dial, zeroes: zeroes, any_zero: any_zero} ->
      dist = String.to_integer(sdist)

      {new_dial, zero_crossing_count} =
        case dir do
          "L" ->
            {Integer.mod(dial - dist, 100), div(Integer.mod(100 - dial, 100) + dist, 100)}

          "R" ->
            {Integer.mod(dial + dist, 100), div(dial + dist, 100)}
        end

      %{
        dial: new_dial,
        zeroes: if(new_dial == 0, do: zeroes + 1, else: zeroes),
        any_zero: zero_crossing_count + any_zero
      }
    end)
  end

  def part_two(problem) do
    state = run(problem)
    state.any_zero
  end
end
1 Like

Hi! Nice to see some solutions to compare with since this is my first time using Elixir. I like it so far :).

This is my solution for day1, I tried to simplify part 2 by replacing the instructions with just +1 and -1. Maybe some of my code is a bit strange since I’m new to Elixir.

defmodule Advent2025Test do
  use ExUnit.Case
  doctest Advent2025

  def day1_data() do
    "day1.txt"
    |> File.stream!()
    |> Stream.map(fn line -> String.split_at(line, 1) end)
    |> Stream.map(fn {dir, num} -> {dir, String.to_integer(String.trim(num))} end)
  end

  def day1_count(data) do
    data
    |> Enum.reduce({50, 0}, fn {dir, num}, {val, res} ->
      Integer.mod(
        val +
          case dir do
            "L" -> -num
            "R" -> num
          end,
        100
      )
      |> then(fn new_val ->
        {new_val, if(new_val == 0, do: res + 1, else: res)}
      end)
    end)
  end

  def day1_make_ticks(data) do
    data
    |> Stream.flat_map(fn {dir, num} ->
      Stream.map(1..num, fn _ -> {dir, 1} end)
    end)
  end

  test "day1_p1" do
    {_, zeroes} = day1_data() |> day1_count()
    IO.puts("answer #{zeroes}")
    assert zeroes == 1048
  end

  test "day1_p2" do
    # Same as part1, but make a list of just {"L", 1} and {"R", 1}
    {_, zeroes} = day1_data() |> day1_make_ticks() |> day1_count()
    IO.puts("answer: #{zeroes}")
    assert zeroes == 6498
  end
end

4 Likes

Not super satisfied with my solution. :cat_face:

defmodule Aoc2025.Solutions.Y25.Day01 do
  alias AoC.Input

  def parse(input, _part) do
    input
    |> Input.read!()
    |> String.trim()
    |> String.split("\n")
    |> Enum.map(fn line ->
      case line do
        "L" <> value -> {:left, String.to_integer(value)}
        "R" <> value -> {:right, String.to_integer(value)}
      end
    end)
  end

  def part_one(problem) do
    count_equilibria(problem, 50, 0, _count_passes? = false)
  end

  def part_two(problem) do
    count_equilibria(problem, 50, 0, _count_passes? = true)
  end

  defp count_equilibria([], _, solution, _count_passes?), do: solution

  defp count_equilibria([{direction, value} | rest], current_position, acc, true) do
    case calculate_position_counting_passes(current_position, direction, value) do
      {0, passes} ->
        count_equilibria(rest, 0, acc + passes, true)

      {new_position, passes} ->
        count_equilibria(rest, new_position, acc + passes, true)
    end
  end

  defp count_equilibria([{direction, value} | rest], current_position, acc, false) do
    case calculate_position(current_position, direction, value) do
      0 -> count_equilibria(rest, 0, acc + 1, false)
      new_position -> count_equilibria(rest, new_position, acc, false)
    end
  end

  defp calculate_position(current_position, :left, value) do
    value = rem(value, 100)

    cond do
      value > current_position ->
        100 - (value - current_position)

      true ->
        current_position - value
    end
  end

  defp calculate_position(current_position, :right, value) do
    rem(current_position + value, 100)
  end

  defp calculate_position_counting_passes(current_position, :left, value) do
    {value, passes} = custom_rem(value, 100)

    cond do
      current_position == 0 ->
        {100 - value, passes}

      value > current_position ->
        {100 - (value - current_position), passes + 1}

      true ->
        passes = if current_position - value == 0, do: passes + 1, else: passes
        {current_position - value, passes}
    end
  end

  defp calculate_position_counting_passes(current_position, :right, value) do
    custom_rem(current_position + value, 100)
  end

  defp custom_rem(dividend, divisor) do
    {rem(dividend, divisor), div(dividend, divisor)}
  end
end

2 Likes

Lots of mod and div’s.
Insight in part 2: when the dail was pointing at 0, it is not an extra click when it goes to a negative number.

Solution in Livebook:

# Advent of code 2025 day 1

```elixir
Mix.install([
  {:kino, "~> 0.18"}
])
```

## Part 1

https://adventofcode.com/2025/day/1

```elixir
input = Kino.Input.textarea("Please give me input:")
```

<!-- livebook:{"reevaluate_automatically":true} -->

```elixir
rotations =
  Kino.Input.read(input)
  |> String.split("\n", trim: true)
  |> Enum.map(fn
    "L" <> rotate -> {-1, String.to_integer(rotate)}
    "R" <> rotate -> {1, String.to_integer(rotate)}
  end)

length(rotations)
```

<!-- livebook:{"reevaluate_automatically":true} -->

```elixir
Enum.reduce(rotations, {0, 50}, fn {sign, rotate}, {null_count, dail_position} ->
  new_position = dail_position + sign * Integer.mod(rotate, 100)
  new_position = if new_position < 0, do: new_position + 100, else: Integer.mod(new_position, 100)
  new_null_count = if new_position == 0, do: null_count + 1, else: null_count
  {new_null_count, new_position}
end)

# prints {null count, last position}
```

## Part 2

<!-- livebook:{"reevaluate_automatically":true} -->

```elixir
Enum.reduce(rotations, {0, 50}, fn {sign, rotate}, {null_count, dail_position} ->
  new_position = dail_position + sign * Integer.mod(rotate, 100)
  extra_counts = div(rotate, 100)
  new_null_count = if (new_position <= 0 and dail_position != 0) or new_position >= 100, do: null_count + 1, else: null_count
  new_position = if new_position < 0, do: new_position + 100, else: Integer.mod(new_position, 100)
  {new_null_count + extra_counts, new_position}
end)

# prints {null count, last position}
```

2 Likes

Part 1

defmodule Combo do
  @modulus 100
  
  def parse(input) do
    Regex.scan(~r"(R|L)(\d+)", input)
      |> Enum.map(fn [_match, dir, n_str] ->
        n = String.to_integer(n_str)
        case dir do
          "R" ->
            n
          "L" ->
            -n
        end
      end)
  end

  def decode(seq) do
    {zeros, _sum} = Enum.reduce(seq, {0, div(@modulus, 2)}, fn clicks, {zs, sum} ->
      sum = rem(sum + clicks, @modulus)
      sum = rem(sum + @modulus, @modulus)
      zeros = if sum == 0 do
        zs + 1
      else
        zs
      end
      {zeros, sum}
    end)
    zeros
  end
end

Part 2

defmodule Combo2 do
  @modulus 100
  
  def decode(seq) do
    {zeros, _sum} = Enum.reduce(seq, {0, div(@modulus, 2)}, fn clicks, {zs, sum} ->
      full_spins = abs(div(clicks, @modulus))
      rem_clicks = rem(clicks, @modulus)
      nsum = sum + rem_clicks
      zeros = if nsum >= @modulus or nsum <= 0 and sum > 0 do
        zs + full_spins + 1
      else
        zs + full_spins
      end
      {zeros, rem(nsum + @modulus, @modulus)}
    end)
    zeros
  end
end
2 Likes

Boilerplate

def file_to_lines(file), do: file |> File.read!() |> String.trim() |> String.split("\n")
def file_to_lines(file, fun), do: file |> file_to_lines() |> Enum.map(fun)

Part 1

def part1(file), do: file |> file_to_lines(&parse/1) |> count0()
def parse(l), do: l |> String.replace("R", "") |> String.replace("L", "-") |> Integer.parse() |> elem(0)
def count0(i), do: i |> Enum.scan(50, &Integer.mod(&1 + &2, 100)) |> Enum.count(&(&1 == 0))

Enum.scan/3 works really well here.

Part 2

def part2(file), do: file |> file_to_lines(&parse/1) |> Stream.flat_map(&flatten/1) |> count0()
def flatten(x), do: if(x < 0, do: List.duplicate(-1, abs(x)), else: List.duplicate(1, x))

It’s tempting to do math, but reusing prior work is simpler if less performant.

5 Likes
defmodule Ben.Day01 do
  @moduledoc ~S"""
  Day 01 - Secret Entrance
  """

  @doc ~S"""
  part1/1 takes a string input containing "a sequence of rotation" needed to open a safe to get the password to open the North Pole Base.
  However, our security training tells us that we just need to count the amount of times that the dial on the safe is left pointing at 0.
  Thus, this function returns the amount of times the dial is left pointing at 0.

  ### Example data
  L68
  L30
  R48
  L5
  R60
  L55
  L1
  L99
  R14
  L8

  ### Example usage
  Input must be a path to a data file in the priv directory.

    iex> part1("priv/data/ben/day_01_example_data.txt")
    3

    iex> part1("hello")
    ** (ArgumentError) input must be a path to a file in the priv directory
  """

  @spec part1(String.t()) :: non_neg_integer
  def part1("priv/" <> _rest_of_path = path_to_data) do
    path_to_data
    |> stream_input()
    |> Enum.reduce({50, 0}, &count_zero_positions/2)
    |> elem(1)
  end

  def part1(_incorrect_input),
    do: raise(ArgumentError, "input must be a path to a file in the priv directory")

  defp count_zero_positions(
         <<direction>> <> increment_string = _instruction,
         {current_position, total_times_pointing_at_zero} = _accumulator
       ) do
    {increments, _} = Integer.parse(increment_string)

    new_position =
      case <<direction>> do
        "L" -> rem(current_position - increments + 100, 100)
        "R" -> rem(current_position + increments, 100)
      end

    total_times_pointing_at_zero =
      if new_position == 0,
        do: 1 + total_times_pointing_at_zero,
        else: total_times_pointing_at_zero

    {new_position, total_times_pointing_at_zero}
  end

  # ------------------------------------------------------------------------------

  @doc ~S"""
  Now we need to count the total amount of times the dial points at 0 when executiong each instruction to get the password to the North Pole Base.

    iex> part2("priv/data/ben/day_01_example_data.txt")
    6

    iex> part2("hello")
    ** (ArgumentError) input must be a path to a file in the priv directory

  """
  def part2("priv/" <> _rest_of_path = path_to_data) do
    path_to_data
    |> stream_input()
    |> Enum.reduce({50, 0}, &count_zero_passes/2)
    |> elem(1)
  end

  def part2(_incorrect_input),
    do: raise(ArgumentError, "input must be a path to a file in the priv directory")

  defp count_zero_passes(
         <<direction>> <> increment_string = _instruction,
         {current_position, total_times_pointing_at_zero} = _accumulator
       ) do
    {increments, _} = Integer.parse(increment_string)
    times_passing_zero = div(increments, 100)
    remaining_increments_to_rotate = rem(increments, 100)

    new_position =
      case <<direction>> do
        "L" -> rem(current_position - remaining_increments_to_rotate + 100, 100)
        "R" -> rem(current_position + remaining_increments_to_rotate, 100)
      end

    additional_times_pointing_at_zero =
      case <<direction>> do
        "L" ->
          if current_position != 0 && current_position - remaining_increments_to_rotate <= 0,
            do: 1 + times_passing_zero,
            else: times_passing_zero

        "R" ->
          if current_position != 0 && current_position + remaining_increments_to_rotate >= 100,
            do: 1 + times_passing_zero,
            else: times_passing_zero
      end

    {new_position, total_times_pointing_at_zero + additional_times_pointing_at_zero}
  end

  # ------------------------------------------------------------------------------

  defp stream_input(input) do
    File.stream!(input) |> Stream.map(&String.trim/1)
  end
end

1 Like

I wanted to play a little bit with nimble_parsec. I will try to use it for every parsing job. Usually I use regexes but 1) time to change, 2) and get a more powerful parsing technique.

#!/usr/bin/env elixir

# Advent of Code 2025. Day 1

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

defmodule M do
  import NimbleParsec

  rotation =
    choice([string("L"), string("R")])
    |> integer(min: 1)

  defparsec :rotation, rotation
end

# Part 1
File.stream!("../day01.txt")
|> Stream.map(fn line -> {:ok, [dir, qty], "\n", %{}, _, _} = M.rotation(line) ; {dir, qty} end)
|> Enum.reduce({50, 0}, fn {dir, qty}, {pos, zeroes} ->
  pos =  Integer.mod(pos + (if dir == "R", do: 1, else: -1) * qty, 100)
  {pos, (if pos == 0, do: zeroes+1, else: zeroes)}
end)
|> tap(fn {_pos, zeroes} -> IO.puts("Day 1. Part 1. Number of zeroes: #{zeroes}") end)

# Part 2
File.stream!("../day01.txt")
|> Stream.map(fn line -> {:ok, [dir, qty], "\n", %{}, _, _} = M.rotation(line) ; {dir, qty} end)
|> Enum.reduce({50, 0}, fn {dir, qty}, {pos, zeroes} ->
  dist_to_zero = if dir == "L", do: pos, else: Integer.mod(100-pos, 100)
  pos =  Integer.mod(pos + (if dir == "R", do: 1, else: -1) * qty, 100)
  n = div(max(0, qty-dist_to_zero), 100) + (if pos != 0 && qty-dist_to_zero >= 0, do: 1, else: 0)
  {pos, zeroes + n}
end)
|> tap(fn {_pos, zeroes} -> IO.puts("Day 1. Part 2. Number of zeroes: #{zeroes}") end)

4 Likes
  defmodule Day1 do
    @input File.read!("day1_1.input")

    @phase1 false

    if @phase1 do
      defp day1_clicks_r(value) when rem(value, 100) == 0, do: 1
      defp day1_clicks_r(_value), do: 0
      defp day1_clicks_l(current, count), do: day1_clicks_r(current - count)
    else
      defp day1_clicks_r(value), do: div(value, 100)
      defp day1_clicks_l(0, count), do: abs(div(count, 100))
      defp day1_clicks_l(current, count), do: abs(div(current - count - 100, 100))
    end

    defp parse_safe_turn(<<"L", count::binary>>, {current, clicks}) do
      count = String.to_integer(count)
      {rem(10_000 + current - count, 100), clicks + day1_clicks_l(current, count)}
    end

    defp parse_safe_turn(<<"R", count::binary>>, {current, clicks}) do
      count = String.to_integer(count)
      {rem(current + count, 100), clicks + day1_clicks_r(current + count)}
    end

    def parse_input(input \\ @input, start \\ 50) do
      ~r/\s+/
      |> Regex.split(input, trim: true)
      |> Enum.reduce({start, 0}, &parse_safe_turn/2)
    end
  end
end
1 Like

Late to the party and expected part 2 to trow in some balls, but could easily adapt (add mode to state, add wildguard check_hit clause)

defmodule Aoc2025.Solutions.Y25.Day01 do
  alias AoC.Input

  @state %{cur: 50, hits: 0, mode: nil}
  @min 0
  @max 99

  def parse(input, _part) do
    Input.read!(input) |> String.trim() |> String.split("\n")
  end

  def part_one(problem), do: run(problem, :p1)
  def part_two(problem), do: run(problem, :p2)

  def run(problem, mode) do
    state =
      Enum.reduce(problem, %{@state | mode: mode}, fn line, acc ->
        {direction, amount} = parse_ln(line)
        goto(acc, direction, amount)
      end)

    state.hits
  end

  defp parse_ln("L" <> amount), do: {-1, String.to_integer(amount)}
  defp parse_ln("R" <> amount), do: {1, String.to_integer(amount)}

  defp turn(%{cur: @min} = state, -1), do: %{state | cur: @max}
  defp turn(%{cur: @max} = state, +1), do: %{state | cur: @min}
  defp turn(state, direction), do: %{state | cur: state.cur + direction}

  defp check_hit(%{cur: 0, mode: :p1} = state, 0), do: %{state | hits: state.hits + 1}
  defp check_hit(%{cur: 0, mode: :p2} = state, _), do: %{state | hits: state.hits + 1}
  defp check_hit(state, _), do: state

  defp goto(state, _, 0),
    do: state

  defp goto(state, dir, amount),
    do: state |> turn(dir) |> check_hit(amount - 1) |> goto(dir, amount - 1)
end

2 Likes

:heart: goto

2 Likes

A little bit late. Also a brute force solution

defmodule AdventOfCode.Solution.Year2025.Day01 do

  def dial(input, start, mult, counter_func) do

    input

    |> String.split("\\n", trim: true)

    |> Enum.map(fn

"L" <> rest -> -1 \* String.to_integer(rest)

"R" <> rest -> String.to_integer(rest)

end)

    |> Enum.reduce(

      {start, 0},

fn delta, {position, score} ->

        {next_position(position, delta, mult), score + counter_func.(position, delta, mult)}

end

    )

    |> elem(1)

end



defp remp(n, p), do: Integer.mod(n, p)

def next_position(current, delta, mult), do: remp(current + delta, mult)



\# Brute force, but simple

def count_hits(start, delta, mult) do

    step = if delta < 0, do: -1, else: 1

    Enum.count((start + step)..(start + delta)//step, fn i -> remp(i, mult) == 0 end)

end



def stop_at_zero(start, delta, mult) do

  if next_position(start, delta, mult) == 0, do: 1, else: 0

end



def part1(input), do: dial(input, 50, 100, &stop_at_zero/3)

def part2(input), do: dial(input, 50, 100, &count_hits/3)

end
1 Like

Another brute force solution (just discovering the forum)

defmodule AdventOfCode.Solution.Year2025.Day01 do
  def dial(input, start, mult, counter_func) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn
      "L" <> rest -> -1 * String.to_integer(rest)
      "R" <> rest -> String.to_integer(rest)
    end)
    |> Enum.reduce(
      {start, 0},
      fn delta, {position, score} ->
        {next_position(position, delta, mult), score + counter_func.(position, delta, mult)}
      end
    )
    |> elem(1)
  end

  defp remp(n, p), do: Integer.mod(n, p)
  def next_position(current, delta, mult), do: remp(current + delta, mult)

  # Brute force, but simple
  def count_hits(start, delta, mult) do
    step = if delta < 0, do: -1, else: 1
    Enum.count((start + step)..(start + delta)//step, fn i -> remp(i, mult) == 0 end)
  end

  def stop_at_zero(start, delta, mult) do
    if next_position(start, delta, mult) == 0, do: 1, else: 0
  end

  def part1(input), do: dial(input, 50, 100, &stop_at_zero/3)
  def part2(input), do: dial(input, 50, 100, &count_hits/3)
end
2 Likes
defmodule Y2025.Day01 do
  def parse(s), do: String.split(s, "\n") |> do_parse()
  def do_parse(l) when is_list(l), do: Enum.map(l, &do_parse/1)
  def do_parse("L" <> num), do: -String.to_integer(num)
  def do_parse("R" <> num), do: String.to_integer(num)

  def part1(s) do
    s
    |> parse()
    |> cum_sum(50)
    |> Enum.count(&(&1 == 0))
  end

  def cum_sum(list, start) do
    list
    |> Enum.reduce([start], fn x, [head | _rest] = list -> [Integer.mod(x + head, 100) | list] end)
  end

  def part2(s) do
    parse(s)
    |> count_rotations(50)
  end

  def count_rotations(list, start) do
    list
    |> Enum.reduce(
      {0, start},
      fn x, {count, cur} ->
        cond do
          x > 0 && cur + x < 100 ->
            {count, cur + x}

          x < 0 && cur + x > 0 ->
            {count, cur + x}

          true ->
            new_head = Integer.mod(cur + x, 100)
            inc = div(abs(cur + x), 100) + if x < 0 && cur != 0, do: 1, else: 0
            {count + inc, new_head}
        end
      end
    )
    |> elem(0)
  end
end

I removed part 1 when doing part 2, so this is only part 1

defmodule Day1 do
  def file, do: Parser.read_file(1)
  def test, do: Parser.read_file("test")

  def parse(input) do
    input
    |> Enum.reject(&is_nil/1)
    |> Enum.map(&get_number/1)
  end

  def get_number("R" <> nb), do: {"R", String.to_integer(nb)}
  def get_number("L" <> nb), do: {"L", String.to_integer(nb)}
  def get_number(a), do: IO.inspect(a)

  def solve(input \\ file()) do
    input
    |> parse()
    |> Enum.reduce({50, 0}, fn instructions, {current_pos, counter} ->
      calculate_next(current_pos, instructions, counter)
    end)
  end

  # because if you start a 0, the go_back_to_100 will consider you need to add one to the counter
  # but you shouldn't because you were already at 0
  def calculate_next(0, {"L", number}, counter) do
    go_back_to_100(0 - number, counter - 1)
  end

  def calculate_next(current_nb, {"L", number}, counter) do
    go_back_to_100(current_nb - number, counter)
  end

  def calculate_next(current_nb, {"R", number}, counter) do
    go_back_to_100(current_nb + number, counter)
  end

  def go_back_to_100(100, counter), do: {0, counter + 1}
  def go_back_to_100(0, counter), do: {0, counter + 1}
  def go_back_to_100(nb, counter) when nb > 99, do: go_back_to_100(nb - 100, counter + 1)
  def go_back_to_100(nb, counter) when nb < 0, do: go_back_to_100(100 + nb, counter + 1)
  def go_back_to_100(nb, counter), do: {nb, counter}
end