defmodule QRCode.GeneratorPolynomial do
  @moduledoc """
  Error correction coding uses polynomial long division.  To do that,
  two polynomials are needed. The first polynomial to use is called the message
  polynomial. The message polynomial uses the data codewords
  from the data encoding step as its coefficients. For example,
  if the data codewords, converted to integers, were *25, 218, and 35,*
  the message polynomial would be *25x2 + 218x + 35.* In practice,
  real message polynomials for standard QR codes are much longer,
  but this is just an example.

  The message polynomial will be divided by a generator polynomial.
  The generator polynomial is a polynomial that is created by multiplying
  `(x - a0) ... (x - a(n-1))`
  where `n` is the number of error correction codewords
  that must be generated (see the error correction table).
  """

  alias QRCode.GaloisField, as: GField

  import Bitwise

  @type degree() :: 1..254
  @type polynomial() :: [GField.alpha()]

  @doc """
  Returns generator polynomials in alpha exponent for given error code length.
  Example:
      iex> QRCode.GeneratorPolynomial.create(10)
      [0, 251, 67, 46, 61, 118, 70, 64, 94, 32, 45]
  """
  @spec create(degree()) :: polynomial()
  def create(degree) when is_integer(degree) and degree in 1..254//1 do
    degree
    |> roots()
    |> Enum.reduce(&multiply/2)
  end

  defp roots(degree, rts \\ [])
  defp roots(1, rts), do: [[0, 0] | rts]

  defp roots(degree, rts) do
    roots(degree - 1, [[0, degree - 1] | rts])
  end

  defp multiply([0, root], poly) do
    root_multiplied = Enum.map(poly, &GField.add(&1, root))

    [last | _] = Enum.reverse(root_multiplied)

    result =
      poly
      |> tl()
      |> Enum.zip(root_multiplied)
      |> Enum.map(fn
        {0, 0} -> 0
        {x, y} -> GField.to_a(bxor(GField.to_i(x), GField.to_i(y)))
      end)

    [0 | result] ++ [last]
  end
end
