Packages
qr_code_term
0.1.0
QRCode is a library for Elixir to create byte-mode QR codes and print them in terminal.
Current section
Files
Jump to
Current section
Files
lib/gf256.ex
defmodule QRCode.GF256 do
@moduledoc """
Galois field(256)
https://en.wikipedia.org/wiki/Finite_field
https://github.com/komone/qrcode/blob/master/src/gf256.erl
"""
defstruct exponent: nil, log: nil
alias QRCode.GF256
require Bitwise
@range 255
def field(prime_modulus) do
exponent = exponent_table(1, prime_modulus, [])
%GF256{
exponent: exponent,
log: log_table(exponent, 1, [0])
}
end
defp exponent_table(x, modulus, acc) when length(acc) <= @range do
x0 = case Bitwise.bsl(x, 1) do
v when v > @range -> Bitwise.bxor(v, modulus)
v -> v
end
exponent_table(x0, modulus, [x|acc])
end
defp exponent_table(_, _, acc), do: Enum.reverse(acc)
defp log_table(e, count, acc) when count <= @range do
x = index_of(count, 0, e)
log_table(e, count + 1, [x | acc])
end
defp log_table(_, _, acc), do: Enum.reverse(acc)
defp index_of(x, count, [x|_]), do: count
defp index_of(x, count, [_|t]), do: index_of(x, count + 1, t)
def add(%GF256{}, a, b) when is_integer(a) and is_integer(b), do:
Bitwise.bxor(a, b)
def add(%GF256{}, [0], b) when is_list(b), do: b
def add(%GF256{}, a, [0]) when is_list(a), do: a
def add(%GF256{} = f, a, b) when is_list(a) and is_list(b), do:
add(f, Enum.reverse(a), Enum.reverse(b), [])
defp add(f, [h|t], [h0|t0], acc), do:
add(f, t, t0, [Bitwise.bxor(h, h0) | acc])
defp add(f, [h|t], [], acc), do:
add(f, t, [], [h|acc])
defp add(f, [], [h|t], acc), do:
add(f, [], t, [h|acc])
defp add(_, [], [], acc), do: acc
def subtract(%GF256{} = f, a, b), do:
add(f, a, b)
def multiply(%GF256{}, 0, _), do: 0
def multiply(%GF256{}, _, 0), do: 0
def multiply(%GF256{} = f, a, b) do
x = rem(log(f, a) + log(f, b), @range)
exponent(f, x)
end
def exponent(%GF256{exponent: e}, n), do:
Enum.at(e, n)
def log(%GF256{log: l}, n), do:
Enum.at(l, n)
def inverse(%GF256{} = f, x), do:
exponent(f, @range - log(f, x))
def value(%GF256{}, poly, 0), do:
List.last(poly)
def value(%GF256{} = f, poly, 1), do:
List.foldl(poly, 0, fn x, sum -> add(f, x, sum) end)
def value(%GF256{} = f, [h|t], x), do:
value(f, t, x, h)
defp value(f, [h|t], x, acc) do
acc = multiply(f, x, acc)
acc = add(f, acc, h)
value(f, t, x, acc)
end
defp value(_, [], _, acc), do: acc
def monomial(%GF256{}, 0, degree) when degree >= 0, do: [0]
def monomial(%GF256{}, coeff, degree) when degree >= 0, do:
[coeff | List.duplicate(0, degree)]
def monomial_product(f, poly, coeff, degree), do:
monomial_product(f, poly, coeff, degree, [])
defp monomial_product(f, [h|t], c, d, acc) do
p = GF256.multiply(f, h, c)
monomial_product(f, t, c, d, [p | acc])
end
defp monomial_product(f, [], c, d, acc) when d > 0, do:
monomial_product(f, [], c, d - 1, [0|acc])
defp monomial_product(_, [], _, 0, acc), do: Enum.reverse(acc)
def polynomial_product(_, [0], _), do: [0]
def polynomial_product(_, _, [0]), do: [0]
def polynomial_product(f, p0, p1), do:
polynomial_product0(f, p0, p1, [], [])
defp polynomial_product0(f, [h|t], p1, p2, acc) do
[h0|t0] = polynomial_product1(f, h, p1, p2, [])
polynomial_product0(f, t, p1, t0, [h0|acc])
end
defp polynomial_product0(f, [], p1, [h|t], acc), do:
polynomial_product0(f, [], p1, t, [h|acc])
defp polynomial_product0(_, [], _, [], acc), do:
Enum.reverse(acc)
defp polynomial_product1(_, _, [], [], acc), do:
Enum.reverse(acc)
defp polynomial_product1(f, x, [h|t], [], acc) do
coeff = polynomial_product2(f, x, h, 0)
polynomial_product1(f, x, t, [], [coeff|acc])
end
defp polynomial_product1(f, x, [h|t], [h0|t0], acc) do
coeff = polynomial_product2(f, x, h, h0)
polynomial_product1(f, x, t, t0, [coeff|acc])
end
defp polynomial_product2(f, x, h, h0) do
coeff = multiply(f, x, h)
add(f, h0, coeff)
end
def divide(%GF256{} = f, a, [h|_] = b) when b != [0] do
idlt = inverse(f, h)
divide(f, idlt, b, [0], a)
end
defp divide(f, idlt, b, q, [h|_] = r)
when length(r) >= length(b) and r != [0] do
diff = length(r) - length(b)
scale = multiply(f, h, idlt)
m = monomial(f, scale, diff)
q = add(f, q, m)
coeffs = monomial_product(f, b, scale, diff)
[_|r] = add(f, r, coeffs)
divide(f, idlt, b, q, r)
end
defp divide(_, _, _, q, r), do: {q, r}
end