diff --git a/.formatter.exs b/.formatter.exs index 525446d..6c1f5d9 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,4 +1,5 @@ -# Used by "mix format" -[ - inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] -] +# Used by "mix format" +[ + inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"], + line_length: 180 +] diff --git a/.gitignore b/.gitignore index 5f4505a..d35a336 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ eqrcode-*.tar .elixir_ls /test/images/ -/test/html/ \ No newline at end of file +/test/html/ +.mix_tasks diff --git a/lib/eqrcode.ex b/lib/eqrcode.ex index c22cb9c..75265ca 100644 --- a/lib/eqrcode.ex +++ b/lib/eqrcode.ex @@ -15,17 +15,20 @@ defmodule EQRCode do alias EQRCode.{Encode, ReedSolomon, Matrix} + @type error_correction_level :: :l | :m | :q | :h + @doc """ Encode the binary. """ - @spec encode(binary) :: Matrix.t() - def encode(bin) when byte_size(bin) <= 154 do - data = - Encode.encode(bin) + @spec encode(binary, error_correction_level()) :: Matrix.t() + def encode(bin, error_correction_level \\ :l) + + def encode(bin, error_correction_level) when byte_size(bin) <= 2952 do + {version, error_correction_level, data} = + Encode.encode(bin, error_correction_level) |> ReedSolomon.encode() - Encode.version(bin) - |> Matrix.new() + Matrix.new(version, error_correction_level) |> Matrix.draw_finder_patterns() |> Matrix.draw_seperators() |> Matrix.draw_alignment_patterns() @@ -39,23 +42,23 @@ defmodule EQRCode do |> Matrix.draw_quite_zone() end - def encode(bin) when is_nil(bin) do + def encode(bin, _error_correction_level) when is_nil(bin) do raise(ArgumentError, message: "you must pass in some input") end - def encode(_), - do: raise(ArgumentError, message: "your input is too long. keep it under 155 characters") + def encode(_, _), + do: raise(ArgumentError, message: "your input is too long. keep it under 2952 characters") @doc """ Encode the binary with custom pattern bits. Only supports version 5. """ - @spec encode(binary, bitstring) :: Matrix.t() - def encode(bin, bits) when byte_size(bin) <= 106 do - data = - Encode.encode(bin, bits) + @spec encode(binary, error_correction_level(), bitstring) :: Matrix.t() + def encode(bin, error_correction_level, bits) when byte_size(bin) <= 106 do + {version, error_correction_level, data} = + Encode.encode(bin, error_correction_level, bits) |> ReedSolomon.encode() - Matrix.new(5) + Matrix.new(version, error_correction_level) |> Matrix.draw_finder_patterns() |> Matrix.draw_seperators() |> Matrix.draw_alignment_patterns() @@ -67,7 +70,7 @@ defmodule EQRCode do |> Matrix.draw_quite_zone() end - def encode(_, _), do: IO.puts("Binary too long.") + def encode(_, _, _), do: IO.puts("Binary too long.") @doc """ ```elixir diff --git a/lib/eqrcode/encode.ex b/lib/eqrcode/encode.ex index 8d38d4c..649f807 100644 --- a/lib/eqrcode/encode.ex +++ b/lib/eqrcode/encode.ex @@ -3,63 +3,57 @@ defmodule EQRCode.Encode do Data encoding in Byte Mode. """ + alias EQRCode.SpecTable import Bitwise - @byte_mode 0b0100 + @error_correction_level SpecTable.error_correction_level() + @pad <<236, 17>> - @capacity_l [0, 17, 32, 53, 78, 106, 134, 154] - @ecc_l %{ - 1 => 19, - 2 => 34, - 3 => 55, - 4 => 80, - 5 => 108, - 6 => 136, - 7 => 156 - } @mask0 <<0x99999999999999666666666666669966666666659999999996699533333333332CCD332CCCCCCCCCCCCCCD333333333333332CCD332CCCCCCCCCCCCCCD333333333333332CCD332CCCCCCCCCCCCCCD333333333333332CCD332CCCCCCCCCCCCCCD333333333333332CCD332CCCCCCCCCCCCCCD33333333333333333332CCCCCCCCCD33333333::1072>> @doc """ Encode the binary. Example: - iex> QRCode.Encode.encode("hello world!") - {1, [0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, + iex> EQRCode.Encode.encode("hello world!", :l) + {1, :l, [0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0]} """ - @spec encode(binary) :: {integer, [0 | 1]} - def encode(bin) do - version = version(bin) + @spec encode(binary, SpecTable.error_correction_level()) :: {SpecTable.version(), SpecTable.error_correction_level(), [0 | 1]} + def encode(bin, error_correction_level) when error_correction_level in @error_correction_level do + {:ok, version} = version(bin, error_correction_level) + cci_len = SpecTable.character_count_indicator_bits(version, error_correction_level) + mode = SpecTable.mode_indicator() encoded = - [<<@byte_mode::4>>, <>, bin, <<0::4>>] + [<>, <>, bin, <<0::4>>] |> Enum.flat_map(&bits/1) - |> pad_bytes(version) + |> pad_bytes(version, error_correction_level) - {version, encoded} + {version, error_correction_level, encoded} end - @doc """ - Encode the binary with custom pattern bits. - """ - @spec encode(binary, bitstring) :: {integer, [0 | 1]} - def encode(bin, bits) do + # Encode the binary with custom pattern bits. + @spec encode(binary, SpecTable.error_correction_level(), bitstring) :: {SpecTable.version(), SpecTable.error_correction_level(), [0 | 1]} + def encode(bin, error_correction_level, bits) when error_correction_level in @error_correction_level do version = 5 n = byte_size(bin) n1 = n + 2 - n2 = @ecc_l[version] - n1 + n2 = SpecTable.code_words_len(version, error_correction_level) - n1 + cci_len = SpecTable.character_count_indicator_bits(version, error_correction_level) + mode = SpecTable.mode_indicator() <<_::binary-size(n1), mask::binary-size(n2), _::binary>> = @mask0 encoded = - <<@byte_mode::4, n::8, bin::binary-size(n), 0::4, xor(bits, mask)::bits>> + <> |> bits() - |> pad_bytes(version) + |> pad_bytes(version, error_correction_level) - {version, encoded} + {version, error_correction_level, encoded} end defp xor(<<>>, _), do: <<>> @@ -73,20 +67,20 @@ defmodule EQRCode.Encode do Returns the lowest version for the given binary. Example: - iex> QRCode.Encode.version("hello world!") - 1 + iex> EQRCode.Encode.version("hello world!", :l) + {:ok, 1} """ - @spec version(binary) :: integer - def version(bin) do - len = byte_size(bin) - Enum.find_index(@capacity_l, &(&1 >= len)) + @spec version(binary, SpecTable.error_correction_level()) :: {:error, :no_version_found} | {:ok, SpecTable.version()} + def version(bin, error_correction_level) do + byte_size(bin) + |> SpecTable.find_version(error_correction_level) end @doc """ Returns bits for any binary data. Example: - iex> QRCode.Encode.bits(<<123, 4>>) + iex> EQRCode.Encode.bits(<<123, 4>>) [0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0] """ @spec bits(bitstring) :: [0 | 1] @@ -94,11 +88,13 @@ defmodule EQRCode.Encode do for <>, do: b end - defp pad_bytes(list, version) do - n = @ecc_l[version] * 8 - length(list) + defp pad_bytes(list, version, error_correction_level) do + n = SpecTable.code_words_len(version, error_correction_level) * 8 - length(list) - Stream.cycle(bits(@pad)) - |> Stream.take(n) - |> (&Enum.concat(list, &1)).() + Enum.concat( + list, + Stream.cycle(bits(@pad)) + |> Stream.take(n) + ) end end diff --git a/lib/eqrcode/galois_field.ex b/lib/eqrcode/galois_field.ex index 211da2a..df92442 100644 --- a/lib/eqrcode/galois_field.ex +++ b/lib/eqrcode/galois_field.ex @@ -3,20 +3,6 @@ defmodule EQRCode.GaloisField do import Bitwise - @integers %{} - @alphas %{} - - Stream.iterate(1, fn e -> - n = e <<< 1 - if n >= 256, do: n ^^^ 0b100011101, else: n - end) - |> Stream.take(256) - |> Stream.with_index() - |> Enum.each(fn {e, i} -> - Module.put_attribute(__MODULE__, :alphas, Map.put(@alphas, e, i)) - Module.put_attribute(__MODULE__, :integers, Map.put(@integers, i, e)) - end) - @doc """ Given alpha exponent returns integer. @@ -25,9 +11,7 @@ defmodule EQRCode.GaloisField do 2 """ @spec to_i(integer) :: integer - def to_i(alpha) do - @integers[alpha] - end + def to_i(alpha) @doc """ Given integer returns alpha exponent. @@ -37,7 +21,15 @@ defmodule EQRCode.GaloisField do 1 """ @spec to_a(integer) :: integer - def to_a(integer) do - @alphas[integer] - end + def to_a(integer) + + Stream.iterate({1, 0}, fn {e, i} -> + n = e <<< 1 + {if(n >= 256, do: n ^^^ 0b100011101, else: n), i + 1} + end) + |> Enum.take(256) + |> Enum.map(fn {e, i} -> + def to_i(unquote(i)), do: unquote(e) + def to_a(unquote(e)), do: unquote(i) + end) end diff --git a/lib/eqrcode/matrix.ex b/lib/eqrcode/matrix.ex index cbf37b0..f7932ed 100644 --- a/lib/eqrcode/matrix.ex +++ b/lib/eqrcode/matrix.ex @@ -1,15 +1,17 @@ defmodule EQRCode.Matrix do @moduledoc false + alias EQRCode.SpecTable + import Bitwise - defstruct [:version, :modules, :mask, :matrix] + @derive {Inspect, only: [:version, :error_correction_level, :modules, :mask]} + defstruct [:version, :error_correction_level, :modules, :mask, :matrix] + @type coordinate :: {non_neg_integer(), non_neg_integer()} @type matrix :: term - @type t :: %__MODULE__{version: integer, modules: integer, matrix: matrix} - @type coordinate :: {integer, integer} + @type t :: %__MODULE__{version: SpecTable.version(), error_correction_level: SpecTable.error_correction_level(), modules: integer, matrix: matrix} - @ecc_l 0b01 @alignments %{ 1 => [], 2 => [6, 18], @@ -17,99 +19,78 @@ defmodule EQRCode.Matrix do 4 => [6, 26], 5 => [6, 30], 6 => [6, 34], - 7 => [6, 22, 38] + 7 => [6, 22, 38], + 8 => [6, 24, 42], + 9 => [6, 26, 46], + 10 => [6, 28, 50], + 11 => [6, 30, 54], + 12 => [6, 32, 58], + 13 => [6, 34, 62], + 14 => [6, 26, 46, 66], + 15 => [6, 26, 48, 70], + 16 => [6, 26, 50, 74], + 17 => [6, 30, 54, 78], + 18 => [6, 30, 56, 82], + 19 => [6, 30, 58, 86], + 20 => [6, 34, 62, 90], + 21 => [6, 28, 50, 72, 94], + 22 => [6, 26, 50, 74, 98], + 23 => [6, 30, 54, 78, 102], + 24 => [6, 28, 54, 80, 106], + 25 => [6, 32, 58, 84, 110], + 26 => [6, 30, 58, 86, 114], + 27 => [6, 34, 62, 90, 118], + 28 => [6, 26, 50, 74, 98, 122], + 29 => [6, 30, 54, 78, 102, 126], + 30 => [6, 26, 52, 78, 104, 130], + 31 => [6, 30, 56, 82, 108, 134], + 32 => [6, 34, 60, 86, 112, 138], + 33 => [6, 30, 58, 86, 114, 142], + 34 => [6, 34, 62, 90, 118, 146], + 35 => [6, 30, 54, 78, 102, 126, 150], + 36 => [6, 24, 50, 76, 102, 128, 154], + 37 => [6, 28, 54, 80, 106, 132, 158], + 38 => [6, 32, 58, 84, 110, 136, 162], + 39 => [6, 26, 54, 82, 110, 138, 166], + 40 => [6, 30, 58, 86, 114, 142, 170] } - @finder_pattern [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 0, - 1, - 1, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 1, - 0, - 1, - 1, - 0, - 1, - 1, - 1, - 0, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 - ] - @alignment_pattern [ - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 1, - 1, - 0, - 1, - 0, - 1, - 1, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 1, - 1 - ] + + @finder_pattern Code.eval_string(""" + [ + 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1 + ] + """) + |> elem(0) + + @alignment_pattern Code.eval_string(""" + [ + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 1, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1, + ] + """) + |> elem(0) @doc """ Initialize the matrix. """ - @spec new(integer) :: t - def new(version) do + @spec new(SpecTable.version(), SpecTable.error_correction_level()) :: t + def new(version, error_correction_level \\ :l) do modules = (version - 1) * 4 + 21 matrix = Tuple.duplicate(nil, modules) |> Tuple.duplicate(modules) - %__MODULE__{version: version, modules: modules, matrix: matrix} + %__MODULE__{version: version, error_correction_level: error_correction_level, modules: modules, matrix: matrix} end @doc """ @@ -305,8 +286,9 @@ defmodule EQRCode.Matrix do Fill the reserved format information areas. """ @spec draw_format_areas(t) :: t - def draw_format_areas(%__MODULE__{matrix: matrix, modules: modules, mask: mask} = m) do - data = EQRCode.ReedSolomon.bch_encode(<<@ecc_l::2, mask::3>>) + def draw_format_areas(%__MODULE__{matrix: matrix, modules: modules, mask: mask, error_correction_level: ecl} = m) do + ecc_l = SpecTable.error_corretion_bits(ecl) + data = EQRCode.ReedSolomon.bch_encode(<>) matrix = [ @@ -331,8 +313,9 @@ defmodule EQRCode.Matrix do @spec draw_version_areas(t) :: t def draw_version_areas(%__MODULE__{version: version} = m) when version < 7, do: m - def draw_version_areas(%__MODULE__{matrix: matrix, modules: modules} = m) do - data = EQRCode.Encode.bits(<<0b000111110010010100::18>>) + def draw_version_areas(%__MODULE__{matrix: matrix, modules: modules, version: version} = m) do + version_information_bits = SpecTable.version_information_bits(version) + data = EQRCode.Encode.bits(<>) z = modules - 9 matrix = diff --git a/lib/eqrcode/reed_solomon.ex b/lib/eqrcode/reed_solomon.ex index e7e280e..2a2b652 100644 --- a/lib/eqrcode/reed_solomon.ex +++ b/lib/eqrcode/reed_solomon.ex @@ -2,17 +2,8 @@ defmodule EQRCode.ReedSolomon do @moduledoc false import Bitwise + alias EQRCode.SpecTable - @rs_block %{ - # version => {error_code_len, data_code_len, remainder_len} - 1 => {07, 019, 0}, - 2 => {10, 034, 7}, - 3 => {15, 055, 7}, - 4 => {20, 080, 7}, - 5 => {26, 108, 7}, - 6 => {18, 068, 7}, - 7 => {20, 078, 0} - } @format_generator_polynomial 0b10100110111 @format_mask 0b101010000010010 @@ -48,31 +39,92 @@ defmodule EQRCode.ReedSolomon do Reed-Solomon encode. Example: - iex> EQRCode.ReedSolomon.encode(EQRCode.Encode.encode("hello world!")) - <<64, 198, 134, 86, 198, 198, 242, 7, 118, 247, 38, 198, 66, 16, - 236, 17, 236, 17, 236, 45, 99, 25, 84, 35, 114, 46>> + iex> EQRCode.ReedSolomon.encode(EQRCode.Encode.encode("hello world!", :l)) + {1, :l, <<64, 198, 134, 86, 198, 198, 242, 7, 118, 247, 38, 198, 66, 16, + 236, 17, 236, 17, 236, 45, 99, 25, 84, 35, 114, 46>>} """ - @spec encode({integer, [0 | 1]}) :: [binary] - def encode({version, message}) do - {error_code_len, data_code_len, remainder_len} = @rs_block[version] - gen_poly = generator_polynomial(error_code_len) + @spec encode({SpecTable.version(), SpecTable.error_correction_level(), [0 | 1]}) :: {SpecTable.version(), SpecTable.error_correction_level(), bitstring()} + def encode({version, error_correction_level, message}) do + remainder_len = SpecTable.remainer(version) data = - Stream.chunk_every(message, 8) - |> Stream.map(&String.to_integer(Enum.join(&1), 2)) - |> Stream.chunk_every(data_code_len) - |> Stream.map(&{&1, polynomial_division(&1, gen_poly, data_code_len)}) + Enum.chunk_every(message, 8) + |> Enum.map(&String.to_integer(Enum.join(&1), 2)) + |> chunk_and_devide_groups(version, error_correction_level) |> Enum.unzip() - |> Tuple.to_list() - |> Enum.flat_map(&interleave/1) + |> interleave() |> :binary.list_to_bin() - <> + {version, error_correction_level, <>} + end + + def count(d) when is_map(d), do: count(d |> Map.values()) + def count(d) when is_tuple(d), do: count(d |> Tuple.to_list()) + def count(d) when is_list(d), do: Enum.reduce(d, 0, fn e, acc -> acc + count(e) end) + def count(_), do: 1 + + def print_count(d) do + count(d) |> IO.inspect(label: "#{__ENV__.file}:#{__ENV__.line}") + d + end + + def chunk_and_devide_groups(enum, version, error_correction_level) do + group1_data_code_len = SpecTable.group1_codewords_per_block(version, error_correction_level) + group1_block_len = SpecTable.group1_block_len(version, error_correction_level) + + group2_data_code_len = SpecTable.group2_codewords_per_block(version, error_correction_level) + group2_block_len = SpecTable.group2_block_len(version, error_correction_level) + + error_code_len = SpecTable.ec_codewords_per_block(version, error_correction_level) + gen_poly = generator_polynomial(error_code_len) + + {group1, enum} = group(enum, group1_block_len, group1_data_code_len) + {group2, _enum} = group(enum, group2_block_len, group2_data_code_len) + + Enum.concat( + group1 + |> Enum.map(&group_devisions(&1, gen_poly, group1_data_code_len)), + group2 + |> Enum.map(&group_devisions(&1, gen_poly, group2_data_code_len)) + ) end - defp interleave(list) do - Enum.zip(list) - |> Enum.flat_map(&Tuple.to_list/1) + defp group(enum, 0, _data_code_len) do + {[], enum} + end + + defp group(enum, block_len, data_code_len) do + { + Enum.take(enum, block_len * data_code_len) + |> Enum.chunk_every(data_code_len), + Enum.drop(enum, block_len * data_code_len) + } + end + + defp group_devisions(group, gen_poly, data_code_len) do + {group, polynomial_division(group, gen_poly, data_code_len)} + end + + def interleave(data, acc \\ []) + def interleave({[], error_code_sec}, acc), do: interleave_sec(error_code_sec, acc) |> Enum.reverse() + def interleave({code_sec, ec}, acc), do: interleave({[], ec}, interleave_sec(code_sec, acc)) + + @doc """ + ## Example + iex> EQRCode.ReedSolomon.interleave_sec([[1, 2], [6, 7], [3, 4, 5], [8, 9, 10]], []) |> Enum.reverse() + [1, 6, 3, 8, 2, 7, 4, 9, 5, 10] + + iex> EQRCode.ReedSolomon.interleave_sec([[]], []) + [] + """ + def interleave_sec([], acc), do: acc + + def interleave_sec(data, acc) do + for [h | tail] <- data do {h, tail} end + |> Enum.unzip() + |> case do + {l, rest} -> interleave_sec(rest, Enum.concat(Enum.reverse(l), acc)) + end end @doc """ @@ -89,15 +141,27 @@ defmodule EQRCode.ReedSolomon do end defp do_polynomial_division([0 | t], _), do: t + defp do_polynomial_division([], _), do: [] defp do_polynomial_division([h | _] = msg, gen_poly) do - Stream.map(gen_poly, &rem(&1 + EQRCode.GaloisField.to_a(h), 255)) + Enum.map(gen_poly, &rem(&1 + EQRCode.GaloisField.to_a(h), 255)) |> Enum.map(&EQRCode.GaloisField.to_i/1) |> pad_zip(msg) |> Enum.map(fn {a, b} -> a ^^^ b end) |> tl() end + # def my_len(v) when is_tuple(v), do: v |> Tuple.to_list() |> Enum.map(&my_len/1) + # def my_len(v = [p|_]) when is_list(p), do: v |> Enum.map(&my_len/1) + # def my_len(v = [p|_]) when is_tuple(p), do: v |> Enum.map(&my_len/1) + # def my_len(v), do: length(v) + + # def len_inspect(v) do + # my_len(v) |> IO.inspect(label: "#{__ENV__.file}:#{__ENV__.line}") + # v + # |> IO.inspect(label: "#{__ENV__.file}:#{__ENV__.line}") + # end + defp pad_zip(left, right) do [short, long] = Enum.sort_by([left, right], &length/1) diff --git a/lib/eqrcode/spec_table.ex b/lib/eqrcode/spec_table.ex new file mode 100644 index 0000000..12c609c --- /dev/null +++ b/lib/eqrcode/spec_table.ex @@ -0,0 +1,1019 @@ +defmodule EQRCode.SpecTable do + @type error_correction_level :: :l | :m | :q | :h + @type version :: 1..40 + @type mode :: :numeric | :alphanumeric | :byte | :kenji | :eci + + def error_correction_level(), do: [:l, :m, :q, :h] + + @mode [ + numeric: 0b0001, + alphanumeric: 0b0010, + byte: 0b0100, + kanji: 0b1000, + eci: 0b0111 + ] + + @error_corretion_bits [ + l: 0b01, + m: 0b00, + q: 0b11, + h: 0b10 + ] + + @version_information_bits [ + {7, 0b000111110010010100}, + {8, 0b001000010110111100}, + {9, 0b001001101010011001}, + {10, 0b001010010011010011}, + {11, 0b001011101111110110}, + {12, 0b001100011101100010}, + {13, 0b001101100001000111}, + {14, 0b001110011000001101}, + {15, 0b001111100100101000}, + {16, 0b010000101101111000}, + {17, 0b010001010001011101}, + {18, 0b010010101000010111}, + {19, 0b010011010100110010}, + {20, 0b010100100110100110}, + {21, 0b010101011010000011}, + {22, 0b010110100011001001}, + {23, 0b010111011111101100}, + {24, 0b011000111011000100}, + {25, 0b011001000111100001}, + {26, 0b011010111110101011}, + {27, 0b011011000010001110}, + {28, 0b011100110000011010}, + {29, 0b011101001100111111}, + {30, 0b011110110101110101}, + {31, 0b011111001001010000}, + {32, 0b100000100111010101}, + {33, 0b100001011011110000}, + {34, 0b100010100010111010}, + {35, 0b100011011110011111}, + {36, 0b100100101100001011}, + {37, 0b100101010000101110}, + {38, 0b100110101001100100}, + {39, 0b100111010101000001}, + {40, 0b101000110001101001} + ] + + @spec version_information_bits(version()) :: 1..1_114_111 + def version_information_bits(version) + + for {version, bits} <- @version_information_bits do + def version_information_bits(unquote(version)), do: unquote(bits) + end + + @spec error_corretion_bits(error_correction_level()) :: 1..3 + def error_corretion_bits(error_correction_level) + + for {level, bits} <- @error_corretion_bits do + def error_corretion_bits(unquote(level)), do: unquote(bits) + end + + def mode_indicator(mode \\ :byte) + + for {mode, mode_indictor} <- @mode do + def mode_indicator(unquote(mode)), do: unquote(mode_indictor) + end + + # {:version, :error_correction_level, :mode, :capacity, :character_count_indicator_bits} + @table [ + {1, :l, :numeric, 41, 10}, + {1, :l, :alphanumeric, 25, 9}, + {1, :l, :byte, 17, 8}, + {1, :l, :kenji, 10, 8}, + {1, :m, :numeric, 34, 10}, + {1, :m, :alphanumeric, 20, 9}, + {1, :m, :byte, 14, 8}, + {1, :m, :kenji, 8, 8}, + {1, :q, :numeric, 27, 10}, + {1, :q, :alphanumeric, 16, 9}, + {1, :q, :byte, 11, 8}, + {1, :q, :kenji, 7, 8}, + {1, :h, :numeric, 17, 10}, + {1, :h, :alphanumeric, 10, 9}, + {1, :h, :byte, 7, 8}, + {1, :h, :kenji, 4, 8}, + {2, :l, :numeric, 77, 10}, + {2, :l, :alphanumeric, 47, 9}, + {2, :l, :byte, 32, 8}, + {2, :l, :kenji, 20, 8}, + {2, :m, :numeric, 63, 10}, + {2, :m, :alphanumeric, 38, 9}, + {2, :m, :byte, 26, 8}, + {2, :m, :kenji, 16, 8}, + {2, :q, :numeric, 48, 10}, + {2, :q, :alphanumeric, 29, 9}, + {2, :q, :byte, 20, 8}, + {2, :q, :kenji, 12, 8}, + {2, :h, :numeric, 34, 10}, + {2, :h, :alphanumeric, 20, 9}, + {2, :h, :byte, 14, 8}, + {2, :h, :kenji, 8, 8}, + {3, :l, :numeric, 127, 10}, + {3, :l, :alphanumeric, 77, 9}, + {3, :l, :byte, 53, 8}, + {3, :l, :kenji, 32, 8}, + {3, :m, :numeric, 101, 10}, + {3, :m, :alphanumeric, 61, 9}, + {3, :m, :byte, 42, 8}, + {3, :m, :kenji, 26, 8}, + {3, :q, :numeric, 77, 10}, + {3, :q, :alphanumeric, 47, 9}, + {3, :q, :byte, 32, 8}, + {3, :q, :kenji, 20, 8}, + {3, :h, :numeric, 58, 10}, + {3, :h, :alphanumeric, 35, 9}, + {3, :h, :byte, 24, 8}, + {3, :h, :kenji, 15, 8}, + {4, :l, :numeric, 187, 10}, + {4, :l, :alphanumeric, 114, 9}, + {4, :l, :byte, 78, 8}, + {4, :l, :kenji, 48, 8}, + {4, :m, :numeric, 149, 10}, + {4, :m, :alphanumeric, 90, 9}, + {4, :m, :byte, 62, 8}, + {4, :m, :kenji, 38, 8}, + {4, :q, :numeric, 111, 10}, + {4, :q, :alphanumeric, 67, 9}, + {4, :q, :byte, 46, 8}, + {4, :q, :kenji, 28, 8}, + {4, :h, :numeric, 82, 10}, + {4, :h, :alphanumeric, 50, 9}, + {4, :h, :byte, 34, 8}, + {4, :h, :kenji, 21, 8}, + {5, :l, :numeric, 255, 10}, + {5, :l, :alphanumeric, 154, 9}, + {5, :l, :byte, 106, 8}, + {5, :l, :kenji, 65, 8}, + {5, :m, :numeric, 202, 10}, + {5, :m, :alphanumeric, 122, 9}, + {5, :m, :byte, 84, 8}, + {5, :m, :kenji, 52, 8}, + {5, :q, :numeric, 144, 10}, + {5, :q, :alphanumeric, 87, 9}, + {5, :q, :byte, 60, 8}, + {5, :q, :kenji, 37, 8}, + {5, :h, :numeric, 106, 10}, + {5, :h, :alphanumeric, 64, 9}, + {5, :h, :byte, 44, 8}, + {5, :h, :kenji, 27, 8}, + {6, :l, :numeric, 322, 10}, + {6, :l, :alphanumeric, 195, 9}, + {6, :l, :byte, 134, 8}, + {6, :l, :kenji, 82, 8}, + {6, :m, :numeric, 255, 10}, + {6, :m, :alphanumeric, 154, 9}, + {6, :m, :byte, 106, 8}, + {6, :m, :kenji, 65, 8}, + {6, :q, :numeric, 178, 10}, + {6, :q, :alphanumeric, 108, 9}, + {6, :q, :byte, 74, 8}, + {6, :q, :kenji, 45, 8}, + {6, :h, :numeric, 139, 10}, + {6, :h, :alphanumeric, 84, 9}, + {6, :h, :byte, 58, 8}, + {6, :h, :kenji, 36, 8}, + {7, :l, :numeric, 370, 10}, + {7, :l, :alphanumeric, 224, 9}, + {7, :l, :byte, 154, 8}, + {7, :l, :kenji, 95, 8}, + {7, :m, :numeric, 293, 10}, + {7, :m, :alphanumeric, 178, 9}, + {7, :m, :byte, 122, 8}, + {7, :m, :kenji, 75, 8}, + {7, :q, :numeric, 207, 10}, + {7, :q, :alphanumeric, 125, 9}, + {7, :q, :byte, 86, 8}, + {7, :q, :kenji, 53, 8}, + {7, :h, :numeric, 154, 10}, + {7, :h, :alphanumeric, 93, 9}, + {7, :h, :byte, 64, 8}, + {7, :h, :kenji, 39, 8}, + {8, :l, :numeric, 461, 10}, + {8, :l, :alphanumeric, 279, 9}, + {8, :l, :byte, 192, 8}, + {8, :l, :kenji, 118, 8}, + {8, :m, :numeric, 365, 10}, + {8, :m, :alphanumeric, 221, 9}, + {8, :m, :byte, 152, 8}, + {8, :m, :kenji, 93, 8}, + {8, :q, :numeric, 259, 10}, + {8, :q, :alphanumeric, 157, 9}, + {8, :q, :byte, 108, 8}, + {8, :q, :kenji, 66, 8}, + {8, :h, :numeric, 202, 10}, + {8, :h, :alphanumeric, 122, 9}, + {8, :h, :byte, 84, 8}, + {8, :h, :kenji, 52, 8}, + {9, :l, :numeric, 552, 10}, + {9, :l, :alphanumeric, 335, 9}, + {9, :l, :byte, 230, 8}, + {9, :l, :kenji, 141, 8}, + {9, :m, :numeric, 432, 10}, + {9, :m, :alphanumeric, 262, 9}, + {9, :m, :byte, 180, 8}, + {9, :m, :kenji, 111, 8}, + {9, :q, :numeric, 312, 10}, + {9, :q, :alphanumeric, 189, 9}, + {9, :q, :byte, 130, 8}, + {9, :q, :kenji, 80, 8}, + {9, :h, :numeric, 235, 10}, + {9, :h, :alphanumeric, 143, 9}, + {9, :h, :byte, 98, 8}, + {9, :h, :kenji, 60, 8}, + {10, :l, :numeric, 652, 12}, + {10, :l, :alphanumeric, 395, 11}, + {10, :l, :byte, 271, 16}, + {10, :l, :kenji, 167, 10}, + {10, :m, :numeric, 513, 12}, + {10, :m, :alphanumeric, 311, 11}, + {10, :m, :byte, 213, 16}, + {10, :m, :kenji, 131, 10}, + {10, :q, :numeric, 364, 12}, + {10, :q, :alphanumeric, 221, 11}, + {10, :q, :byte, 151, 16}, + {10, :q, :kenji, 93, 10}, + {10, :h, :numeric, 288, 12}, + {10, :h, :alphanumeric, 174, 11}, + {10, :h, :byte, 119, 16}, + {10, :h, :kenji, 74, 10}, + {11, :l, :numeric, 772, 12}, + {11, :l, :alphanumeric, 468, 11}, + {11, :l, :byte, 321, 16}, + {11, :l, :kenji, 198, 10}, + {11, :m, :numeric, 604, 12}, + {11, :m, :alphanumeric, 366, 11}, + {11, :m, :byte, 251, 16}, + {11, :m, :kenji, 155, 10}, + {11, :q, :numeric, 427, 12}, + {11, :q, :alphanumeric, 259, 11}, + {11, :q, :byte, 177, 16}, + {11, :q, :kenji, 109, 10}, + {11, :h, :numeric, 331, 12}, + {11, :h, :alphanumeric, 200, 11}, + {11, :h, :byte, 137, 16}, + {11, :h, :kenji, 85, 10}, + {12, :l, :numeric, 883, 12}, + {12, :l, :alphanumeric, 535, 11}, + {12, :l, :byte, 367, 16}, + {12, :l, :kenji, 226, 10}, + {12, :m, :numeric, 691, 12}, + {12, :m, :alphanumeric, 419, 11}, + {12, :m, :byte, 287, 16}, + {12, :m, :kenji, 177, 10}, + {12, :q, :numeric, 489, 12}, + {12, :q, :alphanumeric, 296, 11}, + {12, :q, :byte, 203, 16}, + {12, :q, :kenji, 125, 10}, + {12, :h, :numeric, 374, 12}, + {12, :h, :alphanumeric, 227, 11}, + {12, :h, :byte, 155, 16}, + {12, :h, :kenji, 96, 10}, + {13, :l, :numeric, 1022, 12}, + {13, :l, :alphanumeric, 619, 11}, + {13, :l, :byte, 425, 16}, + {13, :l, :kenji, 262, 10}, + {13, :m, :numeric, 796, 12}, + {13, :m, :alphanumeric, 483, 11}, + {13, :m, :byte, 331, 16}, + {13, :m, :kenji, 204, 10}, + {13, :q, :numeric, 580, 12}, + {13, :q, :alphanumeric, 352, 11}, + {13, :q, :byte, 241, 16}, + {13, :q, :kenji, 149, 10}, + {13, :h, :numeric, 427, 12}, + {13, :h, :alphanumeric, 259, 11}, + {13, :h, :byte, 177, 16}, + {13, :h, :kenji, 109, 10}, + {14, :l, :numeric, 1101, 12}, + {14, :l, :alphanumeric, 667, 11}, + {14, :l, :byte, 458, 16}, + {14, :l, :kenji, 282, 10}, + {14, :m, :numeric, 871, 12}, + {14, :m, :alphanumeric, 528, 11}, + {14, :m, :byte, 362, 16}, + {14, :m, :kenji, 223, 10}, + {14, :q, :numeric, 621, 12}, + {14, :q, :alphanumeric, 376, 11}, + {14, :q, :byte, 258, 16}, + {14, :q, :kenji, 159, 10}, + {14, :h, :numeric, 468, 12}, + {14, :h, :alphanumeric, 283, 11}, + {14, :h, :byte, 194, 16}, + {14, :h, :kenji, 120, 10}, + {15, :l, :numeric, 1250, 12}, + {15, :l, :alphanumeric, 758, 11}, + {15, :l, :byte, 520, 16}, + {15, :l, :kenji, 320, 10}, + {15, :m, :numeric, 991, 12}, + {15, :m, :alphanumeric, 600, 11}, + {15, :m, :byte, 412, 16}, + {15, :m, :kenji, 254, 10}, + {15, :q, :numeric, 703, 12}, + {15, :q, :alphanumeric, 426, 11}, + {15, :q, :byte, 292, 16}, + {15, :q, :kenji, 180, 10}, + {15, :h, :numeric, 530, 12}, + {15, :h, :alphanumeric, 321, 11}, + {15, :h, :byte, 220, 16}, + {15, :h, :kenji, 136, 10}, + {16, :l, :numeric, 1408, 12}, + {16, :l, :alphanumeric, 854, 11}, + {16, :l, :byte, 586, 16}, + {16, :l, :kenji, 361, 10}, + {16, :m, :numeric, 1082, 12}, + {16, :m, :alphanumeric, 656, 11}, + {16, :m, :byte, 450, 16}, + {16, :m, :kenji, 277, 10}, + {16, :q, :numeric, 775, 12}, + {16, :q, :alphanumeric, 470, 11}, + {16, :q, :byte, 322, 16}, + {16, :q, :kenji, 198, 10}, + {16, :h, :numeric, 602, 12}, + {16, :h, :alphanumeric, 365, 11}, + {16, :h, :byte, 250, 16}, + {16, :h, :kenji, 154, 10}, + {17, :l, :numeric, 1548, 12}, + {17, :l, :alphanumeric, 938, 11}, + {17, :l, :byte, 644, 16}, + {17, :l, :kenji, 397, 10}, + {17, :m, :numeric, 1212, 12}, + {17, :m, :alphanumeric, 734, 11}, + {17, :m, :byte, 504, 16}, + {17, :m, :kenji, 310, 10}, + {17, :q, :numeric, 876, 12}, + {17, :q, :alphanumeric, 531, 11}, + {17, :q, :byte, 364, 16}, + {17, :q, :kenji, 224, 10}, + {17, :h, :numeric, 674, 12}, + {17, :h, :alphanumeric, 408, 11}, + {17, :h, :byte, 280, 16}, + {17, :h, :kenji, 173, 10}, + {18, :l, :numeric, 1725, 12}, + {18, :l, :alphanumeric, 1046, 11}, + {18, :l, :byte, 718, 16}, + {18, :l, :kenji, 442, 10}, + {18, :m, :numeric, 1346, 12}, + {18, :m, :alphanumeric, 816, 11}, + {18, :m, :byte, 560, 16}, + {18, :m, :kenji, 345, 10}, + {18, :q, :numeric, 948, 12}, + {18, :q, :alphanumeric, 574, 11}, + {18, :q, :byte, 394, 16}, + {18, :q, :kenji, 243, 10}, + {18, :h, :numeric, 746, 12}, + {18, :h, :alphanumeric, 452, 11}, + {18, :h, :byte, 310, 16}, + {18, :h, :kenji, 191, 10}, + {19, :l, :numeric, 1903, 12}, + {19, :l, :alphanumeric, 1153, 11}, + {19, :l, :byte, 792, 16}, + {19, :l, :kenji, 488, 10}, + {19, :m, :numeric, 1500, 12}, + {19, :m, :alphanumeric, 909, 11}, + {19, :m, :byte, 624, 16}, + {19, :m, :kenji, 384, 10}, + {19, :q, :numeric, 1063, 12}, + {19, :q, :alphanumeric, 644, 11}, + {19, :q, :byte, 442, 16}, + {19, :q, :kenji, 272, 10}, + {19, :h, :numeric, 813, 12}, + {19, :h, :alphanumeric, 493, 11}, + {19, :h, :byte, 338, 16}, + {19, :h, :kenji, 208, 10}, + {20, :l, :numeric, 2061, 12}, + {20, :l, :alphanumeric, 1249, 11}, + {20, :l, :byte, 858, 16}, + {20, :l, :kenji, 528, 10}, + {20, :m, :numeric, 1600, 12}, + {20, :m, :alphanumeric, 970, 11}, + {20, :m, :byte, 666, 16}, + {20, :m, :kenji, 410, 10}, + {20, :q, :numeric, 1159, 12}, + {20, :q, :alphanumeric, 702, 11}, + {20, :q, :byte, 482, 16}, + {20, :q, :kenji, 297, 10}, + {20, :h, :numeric, 919, 12}, + {20, :h, :alphanumeric, 557, 11}, + {20, :h, :byte, 382, 16}, + {20, :h, :kenji, 235, 10}, + {21, :l, :numeric, 2232, 12}, + {21, :l, :alphanumeric, 1352, 11}, + {21, :l, :byte, 929, 16}, + {21, :l, :kenji, 572, 10}, + {21, :m, :numeric, 1708, 12}, + {21, :m, :alphanumeric, 1035, 11}, + {21, :m, :byte, 711, 16}, + {21, :m, :kenji, 438, 10}, + {21, :q, :numeric, 1224, 12}, + {21, :q, :alphanumeric, 742, 11}, + {21, :q, :byte, 509, 16}, + {21, :q, :kenji, 314, 10}, + {21, :h, :numeric, 969, 12}, + {21, :h, :alphanumeric, 587, 11}, + {21, :h, :byte, 403, 16}, + {21, :h, :kenji, 248, 10}, + {22, :l, :numeric, 2409, 12}, + {22, :l, :alphanumeric, 1460, 11}, + {22, :l, :byte, 1003, 16}, + {22, :l, :kenji, 618, 10}, + {22, :m, :numeric, 1872, 12}, + {22, :m, :alphanumeric, 1134, 11}, + {22, :m, :byte, 779, 16}, + {22, :m, :kenji, 480, 10}, + {22, :q, :numeric, 1358, 12}, + {22, :q, :alphanumeric, 823, 11}, + {22, :q, :byte, 565, 16}, + {22, :q, :kenji, 348, 10}, + {22, :h, :numeric, 1056, 12}, + {22, :h, :alphanumeric, 640, 11}, + {22, :h, :byte, 439, 16}, + {22, :h, :kenji, 270, 10}, + {23, :l, :numeric, 2620, 12}, + {23, :l, :alphanumeric, 1588, 11}, + {23, :l, :byte, 1091, 16}, + {23, :l, :kenji, 672, 10}, + {23, :m, :numeric, 2059, 12}, + {23, :m, :alphanumeric, 1248, 11}, + {23, :m, :byte, 857, 16}, + {23, :m, :kenji, 528, 10}, + {23, :q, :numeric, 1468, 12}, + {23, :q, :alphanumeric, 890, 11}, + {23, :q, :byte, 611, 16}, + {23, :q, :kenji, 376, 10}, + {23, :h, :numeric, 1108, 12}, + {23, :h, :alphanumeric, 672, 11}, + {23, :h, :byte, 461, 16}, + {23, :h, :kenji, 284, 10}, + {24, :l, :numeric, 2812, 12}, + {24, :l, :alphanumeric, 1704, 11}, + {24, :l, :byte, 1171, 16}, + {24, :l, :kenji, 721, 10}, + {24, :m, :numeric, 2188, 12}, + {24, :m, :alphanumeric, 1326, 11}, + {24, :m, :byte, 911, 16}, + {24, :m, :kenji, 561, 10}, + {24, :q, :numeric, 1588, 12}, + {24, :q, :alphanumeric, 963, 11}, + {24, :q, :byte, 661, 16}, + {24, :q, :kenji, 407, 10}, + {24, :h, :numeric, 1228, 12}, + {24, :h, :alphanumeric, 744, 11}, + {24, :h, :byte, 511, 16}, + {24, :h, :kenji, 315, 10}, + {25, :l, :numeric, 3057, 12}, + {25, :l, :alphanumeric, 1853, 11}, + {25, :l, :byte, 1273, 16}, + {25, :l, :kenji, 784, 10}, + {25, :m, :numeric, 2395, 12}, + {25, :m, :alphanumeric, 1451, 11}, + {25, :m, :byte, 997, 16}, + {25, :m, :kenji, 614, 10}, + {25, :q, :numeric, 1718, 12}, + {25, :q, :alphanumeric, 1041, 11}, + {25, :q, :byte, 715, 16}, + {25, :q, :kenji, 440, 10}, + {25, :h, :numeric, 1286, 12}, + {25, :h, :alphanumeric, 779, 11}, + {25, :h, :byte, 535, 16}, + {25, :h, :kenji, 330, 10}, + {26, :l, :numeric, 3283, 12}, + {26, :l, :alphanumeric, 1990, 11}, + {26, :l, :byte, 1367, 16}, + {26, :l, :kenji, 842, 10}, + {26, :m, :numeric, 2544, 12}, + {26, :m, :alphanumeric, 1542, 11}, + {26, :m, :byte, 1059, 16}, + {26, :m, :kenji, 652, 10}, + {26, :q, :numeric, 1804, 12}, + {26, :q, :alphanumeric, 1094, 11}, + {26, :q, :byte, 751, 16}, + {26, :q, :kenji, 462, 10}, + {26, :h, :numeric, 1425, 12}, + {26, :h, :alphanumeric, 864, 11}, + {26, :h, :byte, 593, 16}, + {26, :h, :kenji, 365, 10}, + {27, :l, :numeric, 3517, 14}, + {27, :l, :alphanumeric, 2132, 13}, + {27, :l, :byte, 1465, 16}, + {27, :l, :kenji, 902, 12}, + {27, :m, :numeric, 2701, 14}, + {27, :m, :alphanumeric, 1637, 13}, + {27, :m, :byte, 1125, 16}, + {27, :m, :kenji, 692, 12}, + {27, :q, :numeric, 1933, 14}, + {27, :q, :alphanumeric, 1172, 13}, + {27, :q, :byte, 805, 16}, + {27, :q, :kenji, 496, 12}, + {27, :h, :numeric, 1501, 14}, + {27, :h, :alphanumeric, 910, 13}, + {27, :h, :byte, 625, 16}, + {27, :h, :kenji, 385, 12}, + {28, :l, :numeric, 3669, 14}, + {28, :l, :alphanumeric, 2223, 13}, + {28, :l, :byte, 1528, 16}, + {28, :l, :kenji, 940, 12}, + {28, :m, :numeric, 2857, 14}, + {28, :m, :alphanumeric, 1732, 13}, + {28, :m, :byte, 1190, 16}, + {28, :m, :kenji, 732, 12}, + {28, :q, :numeric, 2085, 14}, + {28, :q, :alphanumeric, 1263, 13}, + {28, :q, :byte, 868, 16}, + {28, :q, :kenji, 534, 12}, + {28, :h, :numeric, 1581, 14}, + {28, :h, :alphanumeric, 958, 13}, + {28, :h, :byte, 658, 16}, + {28, :h, :kenji, 405, 12}, + {29, :l, :numeric, 3909, 14}, + {29, :l, :alphanumeric, 2369, 13}, + {29, :l, :byte, 1628, 16}, + {29, :l, :kenji, 1002, 12}, + {29, :m, :numeric, 3035, 14}, + {29, :m, :alphanumeric, 1839, 13}, + {29, :m, :byte, 1264, 16}, + {29, :m, :kenji, 778, 12}, + {29, :q, :numeric, 2181, 14}, + {29, :q, :alphanumeric, 1322, 13}, + {29, :q, :byte, 908, 16}, + {29, :q, :kenji, 559, 12}, + {29, :h, :numeric, 1677, 14}, + {29, :h, :alphanumeric, 1016, 13}, + {29, :h, :byte, 698, 16}, + {29, :h, :kenji, 430, 12}, + {30, :l, :numeric, 4158, 14}, + {30, :l, :alphanumeric, 2520, 13}, + {30, :l, :byte, 1732, 16}, + {30, :l, :kenji, 1066, 12}, + {30, :m, :numeric, 3289, 14}, + {30, :m, :alphanumeric, 1994, 13}, + {30, :m, :byte, 1370, 16}, + {30, :m, :kenji, 843, 12}, + {30, :q, :numeric, 2358, 14}, + {30, :q, :alphanumeric, 1429, 13}, + {30, :q, :byte, 982, 16}, + {30, :q, :kenji, 604, 12}, + {30, :h, :numeric, 1782, 14}, + {30, :h, :alphanumeric, 1080, 13}, + {30, :h, :byte, 742, 16}, + {30, :h, :kenji, 457, 12}, + {31, :l, :numeric, 4417, 14}, + {31, :l, :alphanumeric, 2677, 13}, + {31, :l, :byte, 1840, 16}, + {31, :l, :kenji, 1132, 12}, + {31, :m, :numeric, 3486, 14}, + {31, :m, :alphanumeric, 2113, 13}, + {31, :m, :byte, 1452, 16}, + {31, :m, :kenji, 894, 12}, + {31, :q, :numeric, 2473, 14}, + {31, :q, :alphanumeric, 1499, 13}, + {31, :q, :byte, 1030, 16}, + {31, :q, :kenji, 634, 12}, + {31, :h, :numeric, 1897, 14}, + {31, :h, :alphanumeric, 1150, 13}, + {31, :h, :byte, 790, 16}, + {31, :h, :kenji, 486, 12}, + {32, :l, :numeric, 4686, 14}, + {32, :l, :alphanumeric, 2840, 13}, + {32, :l, :byte, 1952, 16}, + {32, :l, :kenji, 1201, 12}, + {32, :m, :numeric, 3693, 14}, + {32, :m, :alphanumeric, 2238, 13}, + {32, :m, :byte, 1538, 16}, + {32, :m, :kenji, 947, 12}, + {32, :q, :numeric, 2670, 14}, + {32, :q, :alphanumeric, 1618, 13}, + {32, :q, :byte, 1112, 16}, + {32, :q, :kenji, 684, 12}, + {32, :h, :numeric, 2022, 14}, + {32, :h, :alphanumeric, 1226, 13}, + {32, :h, :byte, 842, 16}, + {32, :h, :kenji, 518, 12}, + {33, :l, :numeric, 4965, 14}, + {33, :l, :alphanumeric, 3009, 13}, + {33, :l, :byte, 2068, 16}, + {33, :l, :kenji, 1273, 12}, + {33, :m, :numeric, 3909, 14}, + {33, :m, :alphanumeric, 2369, 13}, + {33, :m, :byte, 1628, 16}, + {33, :m, :kenji, 1002, 12}, + {33, :q, :numeric, 2805, 14}, + {33, :q, :alphanumeric, 1700, 13}, + {33, :q, :byte, 1168, 16}, + {33, :q, :kenji, 719, 12}, + {33, :h, :numeric, 2157, 14}, + {33, :h, :alphanumeric, 1307, 13}, + {33, :h, :byte, 898, 16}, + {33, :h, :kenji, 553, 12}, + {34, :l, :numeric, 5253, 14}, + {34, :l, :alphanumeric, 3183, 13}, + {34, :l, :byte, 2188, 16}, + {34, :l, :kenji, 1347, 12}, + {34, :m, :numeric, 4134, 14}, + {34, :m, :alphanumeric, 2506, 13}, + {34, :m, :byte, 1722, 16}, + {34, :m, :kenji, 1060, 12}, + {34, :q, :numeric, 2949, 14}, + {34, :q, :alphanumeric, 1787, 13}, + {34, :q, :byte, 1228, 16}, + {34, :q, :kenji, 756, 12}, + {34, :h, :numeric, 2301, 14}, + {34, :h, :alphanumeric, 1394, 13}, + {34, :h, :byte, 958, 16}, + {34, :h, :kenji, 590, 12}, + {35, :l, :numeric, 5529, 14}, + {35, :l, :alphanumeric, 3351, 13}, + {35, :l, :byte, 2303, 16}, + {35, :l, :kenji, 1417, 12}, + {35, :m, :numeric, 4343, 14}, + {35, :m, :alphanumeric, 2632, 13}, + {35, :m, :byte, 1809, 16}, + {35, :m, :kenji, 1113, 12}, + {35, :q, :numeric, 3081, 14}, + {35, :q, :alphanumeric, 1867, 13}, + {35, :q, :byte, 1283, 16}, + {35, :q, :kenji, 790, 12}, + {35, :h, :numeric, 2361, 14}, + {35, :h, :alphanumeric, 1431, 13}, + {35, :h, :byte, 983, 16}, + {35, :h, :kenji, 605, 12}, + {36, :l, :numeric, 5836, 14}, + {36, :l, :alphanumeric, 3537, 13}, + {36, :l, :byte, 2431, 16}, + {36, :l, :kenji, 1496, 12}, + {36, :m, :numeric, 4588, 14}, + {36, :m, :alphanumeric, 2780, 13}, + {36, :m, :byte, 1911, 16}, + {36, :m, :kenji, 1176, 12}, + {36, :q, :numeric, 3244, 14}, + {36, :q, :alphanumeric, 1966, 13}, + {36, :q, :byte, 1351, 16}, + {36, :q, :kenji, 832, 12}, + {36, :h, :numeric, 2524, 14}, + {36, :h, :alphanumeric, 1530, 13}, + {36, :h, :byte, 1051, 16}, + {36, :h, :kenji, 647, 12}, + {37, :l, :numeric, 6153, 14}, + {37, :l, :alphanumeric, 3729, 13}, + {37, :l, :byte, 2563, 16}, + {37, :l, :kenji, 1577, 12}, + {37, :m, :numeric, 4775, 14}, + {37, :m, :alphanumeric, 2894, 13}, + {37, :m, :byte, 1989, 16}, + {37, :m, :kenji, 1224, 12}, + {37, :q, :numeric, 3417, 14}, + {37, :q, :alphanumeric, 2071, 13}, + {37, :q, :byte, 1423, 16}, + {37, :q, :kenji, 876, 12}, + {37, :h, :numeric, 2625, 14}, + {37, :h, :alphanumeric, 1591, 13}, + {37, :h, :byte, 1093, 16}, + {37, :h, :kenji, 673, 12}, + {38, :l, :numeric, 6479, 14}, + {38, :l, :alphanumeric, 3927, 13}, + {38, :l, :byte, 2699, 16}, + {38, :l, :kenji, 1661, 12}, + {38, :m, :numeric, 5039, 14}, + {38, :m, :alphanumeric, 3054, 13}, + {38, :m, :byte, 2099, 16}, + {38, :m, :kenji, 1292, 12}, + {38, :q, :numeric, 3599, 14}, + {38, :q, :alphanumeric, 2181, 13}, + {38, :q, :byte, 1499, 16}, + {38, :q, :kenji, 923, 12}, + {38, :h, :numeric, 2735, 14}, + {38, :h, :alphanumeric, 1658, 13}, + {38, :h, :byte, 1139, 16}, + {38, :h, :kenji, 701, 12}, + {39, :l, :numeric, 6743, 14}, + {39, :l, :alphanumeric, 4087, 13}, + {39, :l, :byte, 2809, 16}, + {39, :l, :kenji, 1729, 12}, + {39, :m, :numeric, 5313, 14}, + {39, :m, :alphanumeric, 3220, 13}, + {39, :m, :byte, 2213, 16}, + {39, :m, :kenji, 1362, 12}, + {39, :q, :numeric, 3791, 14}, + {39, :q, :alphanumeric, 2298, 13}, + {39, :q, :byte, 1579, 16}, + {39, :q, :kenji, 972, 12}, + {39, :h, :numeric, 2927, 14}, + {39, :h, :alphanumeric, 1774, 13}, + {39, :h, :byte, 1219, 16}, + {39, :h, :kenji, 750, 12}, + {40, :l, :numeric, 7089, 14}, + {40, :l, :alphanumeric, 4296, 13}, + {40, :l, :byte, 2953, 16}, + {40, :l, :kenji, 1817, 12}, + {40, :m, :numeric, 5596, 14}, + {40, :m, :alphanumeric, 3391, 13}, + {40, :m, :byte, 2331, 16}, + {40, :m, :kenji, 1435, 12}, + {40, :q, :numeric, 3993, 14}, + {40, :q, :alphanumeric, 2420, 13}, + {40, :q, :byte, 1663, 16}, + {40, :q, :kenji, 1024, 12}, + {40, :h, :numeric, 3057, 14}, + {40, :h, :alphanumeric, 1852, 13}, + {40, :h, :byte, 1273, 16}, + {40, :h, :kenji, 784, 12} + ] + + @ec_levels @table |> Enum.map(&elem(&1, 1)) |> Enum.uniq() + @modes @table |> Enum.map(&elem(&1, 2)) |> Enum.uniq() + + @spec find_version(non_neg_integer(), error_correction_level(), mode()) :: {:error, :no_version_found} | {:ok, version()} + def find_version(bin_len, ec_level \\ :h, mode \\ :byte) + + for ec_level <- @ec_levels, + mode <- @modes, + {version, _, _, cap, _} <- + Enum.filter(@table, &match?({_, ^ec_level, ^mode, _, _}, &1)) |> Enum.sort_by(&elem(&1, 3)) do + def find_version(bin_len, unquote(ec_level), unquote(mode)) when bin_len <= unquote(cap), + do: {:ok, unquote(version)} + end + + def find_version(_bin_len, _ec_level, _mode), do: {:error, :no_version_found} + + @spec character_count_indicator_bits(version(), error_correction_level(), mode()) :: non_neg_integer() + def character_count_indicator_bits(version, ec_level, mode \\ :byte) + + for {version, ec_level, mode, _, cci_len} <- @table do + def character_count_indicator_bits(unquote(version), unquote(ec_level), unquote(mode)), do: unquote(cci_len) + end + + def character_count_indicator_bits(_version, _ec_level, _mode), do: 0 + + # {:version, :error_correction_level, :ec_codewords_per_block, :group1_block_len, :group1_codewords_per_block, :group2_block_len, :group2_codewords_per_block} + @error_correction_table [ + {1, :l, 7, 1, 19, 0, 0}, + {1, :m, 10, 1, 16, 0, 0}, + {1, :q, 13, 1, 13, 0, 0}, + {1, :h, 17, 1, 9, 0, 0}, + {2, :l, 10, 1, 34, 0, 0}, + {2, :m, 16, 1, 28, 0, 0}, + {2, :q, 22, 1, 22, 0, 0}, + {2, :h, 28, 1, 16, 0, 0}, + {3, :l, 15, 1, 55, 0, 0}, + {3, :m, 26, 1, 44, 0, 0}, + {3, :q, 18, 2, 17, 0, 0}, + {3, :h, 22, 2, 13, 0, 0}, + {4, :l, 20, 1, 80, 0, 0}, + {4, :m, 18, 2, 32, 0, 0}, + {4, :q, 26, 2, 24, 0, 0}, + {4, :h, 16, 4, 9, 0, 0}, + {5, :l, 26, 1, 108, 0, 0}, + {5, :m, 24, 2, 43, 0, 0}, + {5, :q, 18, 2, 15, 2, 16}, + {5, :h, 22, 2, 11, 2, 12}, + {6, :l, 18, 2, 68, 0, 0}, + {6, :m, 16, 4, 27, 0, 0}, + {6, :q, 24, 4, 19, 0, 0}, + {6, :h, 28, 4, 15, 0, 0}, + {7, :l, 20, 2, 78, 0, 0}, + {7, :m, 18, 4, 31, 0, 0}, + {7, :q, 18, 2, 14, 4, 15}, + {7, :h, 26, 4, 13, 1, 14}, + {8, :l, 24, 2, 97, 0, 0}, + {8, :m, 22, 2, 38, 2, 39}, + {8, :q, 22, 4, 18, 2, 19}, + {8, :h, 26, 4, 14, 2, 15}, + {9, :l, 30, 2, 116, 0, 0}, + {9, :m, 22, 3, 36, 2, 37}, + {9, :q, 20, 4, 16, 4, 17}, + {9, :h, 24, 4, 12, 4, 13}, + {10, :l, 18, 2, 68, 2, 69}, + {10, :m, 26, 4, 43, 1, 44}, + {10, :q, 24, 6, 19, 2, 20}, + {10, :h, 28, 6, 15, 2, 16}, + {11, :l, 20, 4, 81, 0, 0}, + {11, :m, 30, 1, 50, 4, 51}, + {11, :q, 28, 4, 22, 4, 23}, + {11, :h, 24, 3, 12, 8, 13}, + {12, :l, 24, 2, 92, 2, 93}, + {12, :m, 22, 6, 36, 2, 37}, + {12, :q, 26, 4, 20, 6, 21}, + {12, :h, 28, 7, 14, 4, 15}, + {13, :l, 26, 4, 107, 0, 0}, + {13, :m, 22, 8, 37, 1, 38}, + {13, :q, 24, 8, 20, 4, 21}, + {13, :h, 22, 12, 11, 4, 12}, + {14, :l, 30, 3, 115, 1, 116}, + {14, :m, 24, 4, 40, 5, 41}, + {14, :q, 20, 11, 16, 5, 17}, + {14, :h, 24, 11, 12, 5, 13}, + {15, :l, 22, 5, 87, 1, 88}, + {15, :m, 24, 5, 41, 5, 42}, + {15, :q, 30, 5, 24, 7, 25}, + {15, :h, 24, 11, 12, 7, 13}, + {16, :l, 24, 5, 98, 1, 99}, + {16, :m, 28, 7, 45, 3, 46}, + {16, :q, 24, 15, 19, 2, 20}, + {16, :h, 30, 3, 15, 13, 16}, + {17, :l, 28, 1, 107, 5, 108}, + {17, :m, 28, 10, 46, 1, 47}, + {17, :q, 28, 1, 22, 15, 23}, + {17, :h, 28, 2, 14, 17, 15}, + {18, :l, 30, 5, 120, 1, 121}, + {18, :m, 26, 9, 43, 4, 44}, + {18, :q, 28, 17, 22, 1, 23}, + {18, :h, 28, 2, 14, 19, 15}, + {19, :l, 28, 3, 113, 4, 114}, + {19, :m, 26, 3, 44, 11, 45}, + {19, :q, 26, 17, 21, 4, 22}, + {19, :h, 26, 9, 13, 16, 14}, + {20, :l, 28, 3, 107, 5, 108}, + {20, :m, 26, 3, 41, 13, 42}, + {20, :q, 30, 15, 24, 5, 25}, + {20, :h, 28, 15, 15, 10, 16}, + {21, :l, 28, 4, 116, 4, 117}, + {21, :m, 26, 17, 42, 0, 0}, + {21, :q, 28, 17, 22, 6, 23}, + {21, :h, 30, 19, 16, 6, 17}, + {22, :l, 28, 2, 111, 7, 112}, + {22, :m, 28, 17, 46, 0, 0}, + {22, :q, 30, 7, 24, 16, 25}, + {22, :h, 24, 34, 13, 0, 0}, + {23, :l, 30, 4, 121, 5, 122}, + {23, :m, 28, 4, 47, 14, 48}, + {23, :q, 30, 11, 24, 14, 25}, + {23, :h, 30, 16, 15, 14, 16}, + {24, :l, 30, 6, 117, 4, 118}, + {24, :m, 28, 6, 45, 14, 46}, + {24, :q, 30, 11, 24, 16, 25}, + {24, :h, 30, 30, 16, 2, 17}, + {25, :l, 26, 8, 106, 4, 107}, + {25, :m, 28, 8, 47, 13, 48}, + {25, :q, 30, 7, 24, 22, 25}, + {25, :h, 30, 22, 15, 13, 16}, + {26, :l, 28, 10, 114, 2, 115}, + {26, :m, 28, 19, 46, 4, 47}, + {26, :q, 28, 28, 22, 6, 23}, + {26, :h, 30, 33, 16, 4, 17}, + {27, :l, 30, 8, 122, 4, 123}, + {27, :m, 28, 22, 45, 3, 46}, + {27, :q, 30, 8, 23, 26, 24}, + {27, :h, 30, 12, 15, 28, 16}, + {28, :l, 30, 3, 117, 10, 118}, + {28, :m, 28, 3, 45, 23, 46}, + {28, :q, 30, 4, 24, 31, 25}, + {28, :h, 30, 11, 15, 31, 16}, + {29, :l, 30, 7, 116, 7, 117}, + {29, :m, 28, 21, 45, 7, 46}, + {29, :q, 30, 1, 23, 37, 24}, + {29, :h, 30, 19, 15, 26, 16}, + {30, :l, 30, 5, 115, 10, 116}, + {30, :m, 28, 19, 47, 10, 48}, + {30, :q, 30, 15, 24, 25, 25}, + {30, :h, 30, 23, 15, 25, 16}, + {31, :l, 30, 13, 115, 3, 116}, + {31, :m, 28, 2, 46, 29, 47}, + {31, :q, 30, 42, 24, 1, 25}, + {31, :h, 30, 23, 15, 28, 16}, + {32, :l, 30, 17, 115, 0, 0}, + {32, :m, 28, 10, 46, 23, 47}, + {32, :q, 30, 10, 24, 35, 25}, + {32, :h, 30, 19, 15, 35, 16}, + {33, :l, 30, 17, 115, 1, 116}, + {33, :m, 28, 14, 46, 21, 47}, + {33, :q, 30, 29, 24, 19, 25}, + {33, :h, 30, 11, 15, 46, 16}, + {34, :l, 30, 13, 115, 6, 116}, + {34, :m, 28, 14, 46, 23, 47}, + {34, :q, 30, 44, 24, 7, 25}, + {34, :h, 30, 59, 16, 1, 17}, + {35, :l, 30, 12, 121, 7, 122}, + {35, :m, 28, 12, 47, 26, 48}, + {35, :q, 30, 39, 24, 14, 25}, + {35, :h, 30, 22, 15, 41, 16}, + {36, :l, 30, 6, 121, 14, 122}, + {36, :m, 28, 6, 47, 34, 48}, + {36, :q, 30, 46, 24, 10, 25}, + {36, :h, 30, 2, 15, 64, 16}, + {37, :l, 30, 17, 122, 4, 123}, + {37, :m, 28, 29, 46, 14, 47}, + {37, :q, 30, 49, 24, 10, 25}, + {37, :h, 30, 24, 15, 46, 16}, + {38, :l, 30, 4, 122, 18, 123}, + {38, :m, 28, 13, 46, 32, 47}, + {38, :q, 30, 48, 24, 14, 25}, + {38, :h, 30, 42, 15, 32, 16}, + {39, :l, 30, 20, 117, 4, 118}, + {39, :m, 28, 40, 47, 7, 48}, + {39, :q, 30, 43, 24, 22, 25}, + {39, :h, 30, 10, 15, 67, 16}, + {40, :l, 30, 19, 118, 6, 119}, + {40, :m, 28, 18, 47, 31, 48}, + {40, :q, 30, 34, 24, 34, 25}, + {40, :h, 30, 20, 15, 61, 16} + ] + + @spec code_words_len(version(), error_correction_level()) :: non_neg_integer() + def code_words_len(version, error_correction_level) + + for {version, error_correction_level, _, g1_blocks, g1_codewords, g2_blocks, g2_codewords} <- @error_correction_table do + def code_words_len(unquote(version), unquote(error_correction_level)), do: unquote(g1_blocks * g1_codewords + g2_blocks * g2_codewords) + end + + def code_words_len(_version, _error_correction_level), do: 0 + + @spec ec_codewords_per_block(version(), error_correction_level()) :: non_neg_integer() + def ec_codewords_per_block(version, error_correction_level) + + for {version, error_correction_level, ec_codewords_per_block, _, _, _, _} <- @error_correction_table do + def ec_codewords_per_block(unquote(version), unquote(error_correction_level)), do: unquote(ec_codewords_per_block) + end + + def ec_codewords_per_block(_version, _error_correction_level), do: 0 + + @spec group1_block_len(version(), error_correction_level()) :: non_neg_integer() + def group1_block_len(version, error_correction_level) + + for {version, error_correction_level, _, group1_block_len, _, _, _} <- @error_correction_table do + def group1_block_len(unquote(version), unquote(error_correction_level)), do: unquote(group1_block_len) + end + + def group1_block_len(_version, _error_correction_level), do: 0 + + @spec group1_codewords_per_block(version(), error_correction_level()) :: non_neg_integer() + def group1_codewords_per_block(version, error_correction_level) + + for {version, error_correction_level, _, _, group1_codewords_per_block, _, _} <- @error_correction_table do + def group1_codewords_per_block(unquote(version), unquote(error_correction_level)), do: unquote(group1_codewords_per_block) + end + + def group1_codewords_per_block(_version, _error_correction_level), do: 0 + + @spec group2_block_len(version(), error_correction_level()) :: non_neg_integer() + def group2_block_len(version, error_correction_level) + + for {version, error_correction_level, _, _, _, group2_block_len, _} <- @error_correction_table do + def group2_block_len(unquote(version), unquote(error_correction_level)), do: unquote(group2_block_len) + end + + def group2_block_len(_version, _error_correction_level), do: 0 + + @spec group2_codewords_per_block(version(), error_correction_level()) :: non_neg_integer() + def group2_codewords_per_block(version, error_correction_level) + + for {version, error_correction_level, _, _, _, _, group2_codewords_per_block} <- @error_correction_table do + def group2_codewords_per_block(unquote(version), unquote(error_correction_level)), do: unquote(group2_codewords_per_block) + end + + def group2_codewords_per_block(_version, _error_correction_level), do: 0 + + # {:version, :remainer} + @remainer [ + {1, 0}, + {2, 7}, + {3, 7}, + {4, 7}, + {5, 7}, + {6, 7}, + {7, 0}, + {8, 0}, + {9, 0}, + {10, 0}, + {11, 0}, + {12, 0}, + {13, 0}, + {14, 3}, + {15, 3}, + {16, 3}, + {17, 3}, + {18, 3}, + {19, 3}, + {20, 3}, + {21, 4}, + {22, 4}, + {23, 4}, + {24, 4}, + {25, 4}, + {26, 4}, + {27, 4}, + {28, 3}, + {29, 3}, + {30, 3}, + {31, 3}, + {32, 3}, + {33, 3}, + {34, 3}, + {35, 0}, + {36, 0}, + {37, 0}, + {38, 0}, + {39, 0}, + {40, 0} + ] + + @spec remainer(any) :: 0..7 + def remainer(_version) + + for {version, remainer} <- @remainer do + def remainer(unquote(version)), do: unquote(remainer) + end + + def remainer(_version), do: 0 +end diff --git a/test/eqrcode_test.exs b/test/eqrcode_test.exs index 9433251..bf5d688 100644 --- a/test/eqrcode_test.exs +++ b/test/eqrcode_test.exs @@ -1,5 +1,6 @@ defmodule EQRCodeTest do use ExUnit.Case + doctest EQRCode.Encode describe "encoder" do @sample_matrix {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -37,9 +38,9 @@ defmodule EQRCodeTest do end test "should return error when the input is too long" do - long_qr_content = String.duplicate("a", 160) + long_qr_content = String.duplicate("a", 2954) - assert_raise ArgumentError, "your input is too long. keep it under 155 characters", fn -> + assert_raise ArgumentError, "your input is too long. keep it under 2952 characters", fn -> long_qr_content |> EQRCode.encode() end end