defmodule QRCode.Polynom do
  @moduledoc """
  An polynom math library
  """

  alias QRCode.GaloisField, as: GF
  alias QRCode.GeneratorPolynomial, as: GP

  import Bitwise

  @spec div([GF.value()], GP.polynomial()) :: [GF.value()]
  def div(dividend, divisor) do
    dividend
    |> Stream.iterate(&do_div(&1, divisor))
    |> Enum.at(Enum.count(dividend))
    |> fill_to_degree(Enum.count(divisor) - 1)
  end

  defp do_div([0 | t], _), do: t

  defp do_div([first | _] = dividend, divisor) do
    divisor
    |> Enum.map(fn val -> val |> GF.add(GF.to_a(first)) |> GF.to_i() end)
    |> zip(dividend)
    |> Enum.map(fn {a, b} -> bxor(a, b) end)
    |> tl()
  end

  defp zip(left, right) do
    [short, long] = Enum.sort_by([left, right], &length/1)

    short
    |> Stream.concat(Stream.cycle([0]))
    |> Stream.zip(long)
  end

  defp fill_to_degree(list, degree) when length(list) < degree do
    list ++ List.duplicate(0, degree - Enum.count(list))
  end

  defp fill_to_degree(result, _) do
    result
  end
end
