├── .gitignore ├── Emakefile ├── ebin └── qrcode.app ├── LICENSE ├── src ├── qrcode.hrl ├── bits.erl ├── qrcode_reedsolomon.erl ├── base32.erl ├── qrcode_demo.erl ├── gf256.erl ├── qrcode.erl ├── qrcode_mask.erl ├── qrcode_matrix.erl └── qrcode_params.hrl └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | *.dump 3 | *.lnk 4 | *.log 5 | *.pdf 6 | *.pem 7 | *.png 8 | test/* 9 | work/* 10 | -------------------------------------------------------------------------------- /Emakefile: -------------------------------------------------------------------------------- 1 | % -*- mode:erlang -*- 2 | {"src/*", [{i, "include"}, {outdir, "ebin"}, debug_info, strict_record_tests]}. 3 | -------------------------------------------------------------------------------- /ebin/qrcode.app: -------------------------------------------------------------------------------- 1 | {application, qrcode, 2 | [{description, "QRCode Encoder"}, 3 | {vsn, "1.0.3"}, 4 | {modules, [qrcode, qrcode_matrix, qrcode_mask, qrcode_reedsolomon, gf256, bits, base32]}, 5 | {mod, {qrcode, []}}, 6 | {registered, []}, 7 | {env, []}, 8 | {applications, [kernel, stdlib]} 9 | ]}. 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 Steve Davis 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | -------------------------------------------------------------------------------- /src/qrcode.hrl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | % 15 | % qrcode.hrl 16 | 17 | %% API Record 18 | -record(qrcode, {version, ecc, dimension, data}). 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QR Code Encoder 2 | =============== 3 | 4 | Reference used was ISO/IEC 18004, 1st Edition (2000) 5 | 6 | This implementation is informed by my specific needs, i.e. to provide 7 | two-factor authentication for mobile phones running Google Authenticator. 8 | 9 | + "Byte" mode only (don't need e.g. numeric mode or kanji mode). 10 | + Encode only (no detection/decode). 11 | + Basic supporting library functions provided (HOTP, PNG image functions) to allow full-cyle demo. 12 | 13 | Demo 14 | ==== 15 | 16 | 1. Download repo and compile with `erl -make` 17 | 2. Install Google Authenticator App on your mobile: 18 | + iPhone: http://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8 19 | + Android: https://market.android.com/details?id=com.google.android.apps.authenticator 20 | 3. Run demo: `qrcode_demo:run().` 21 | 4. Open the generated `qrcode.png` file 22 | 5. Scan the qrcode into the phone. 23 | 6. Ensure server clock is correct. 24 | 7. The value of `qrcode_demo:totp()` should show the same passcode as the phone. 25 | 8. Handle PINs/logins for the second part of the "two factor" according to your application design. 26 | 27 | NOTE: This documentation is rather basic as this was open-sourced by specific request! 28 | -------------------------------------------------------------------------------- /src/bits.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | 15 | -module(bits). 16 | 17 | -export([reverse/1, duplicate/2, append/1, bitlist/1, bitstring/1, stringbits/1]). 18 | -compile(export_all). 19 | 20 | reverse(Bin) -> 21 | reverse(Bin, <<>>). 22 | reverse(<>, Acc) -> 23 | reverse(Bin, <>); 24 | reverse(<<>>, Acc) -> 25 | Acc. 26 | 27 | %% 28 | duplicate(Bin, N) -> 29 | duplicate(Bin, N, <<>>). 30 | duplicate(Bin, N, Acc) when N > 0 -> 31 | duplicate(Bin, N - 1, <>); 32 | duplicate(_, 0, Acc) -> 33 | Acc. 34 | 35 | %% 36 | append(List) -> 37 | append(List, <<>>). 38 | append([H|T], Acc) -> 39 | append(T, <>); 40 | append([], Acc) -> 41 | Acc. 42 | 43 | %% 44 | bitlist(Bin) -> 45 | bitlist(Bin, []). 46 | bitlist(<>, Acc) -> 47 | bitlist(Bin, [X|Acc]); 48 | bitlist(<<>>, Acc) -> 49 | lists:reverse(Acc). 50 | 51 | %% 52 | bitstring(Bin) -> 53 | bitstring(Bin, <<>>). 54 | bitstring(<<0:1, Bin/bits>>, Acc) -> 55 | bitstring(Bin, <>); 56 | bitstring(<<1:1, Bin/bits>>, Acc) -> 57 | bitstring(Bin, <>); 58 | bitstring(<<>>, Acc) -> 59 | Acc. 60 | 61 | %% 62 | stringbits(Bin) -> 63 | stringbits(Bin, <<>>). 64 | stringbits(<<$0, Bin/binary>>, Acc) -> 65 | stringbits(Bin, <>); 66 | stringbits(<<$1, Bin/binary>>, Acc) -> 67 | stringbits(Bin, <>); 68 | stringbits(<<>>, Acc) -> 69 | Acc. 70 | -------------------------------------------------------------------------------- /src/qrcode_reedsolomon.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | 15 | -module(qrcode_reedsolomon). 16 | 17 | -export([encode/2, bch_code/2]). 18 | 19 | -define(QRCODE_GF256_PRIME_MODULUS, 285). % 16#011D = 2^8 + 2^4 + 2^3 + 2^2 + 2^0 20 | 21 | %% 22 | encode(Bin, Degree) when Degree > 0 -> 23 | Field = gf256:field(?QRCODE_GF256_PRIME_MODULUS), 24 | Generator = generator(Field, Degree), 25 | Data = binary_to_list(Bin), 26 | Coeffs = gf256:monomial_product(Field, Data, 1, Degree), 27 | {_Quotient, Remainder} = gf256:divide(Field, Coeffs, Generator), 28 | ErrorCorrectionBytes = list_to_binary(Remainder), 29 | <>. 30 | 31 | %% 32 | bch_code(Byte, Poly) -> 33 | MSB = msb(Poly), 34 | Byte0 = Byte bsl (MSB - 1), 35 | bch_code(Byte0, Poly, MSB). 36 | 37 | 38 | %% Internal 39 | 40 | %% 41 | generator(F, D) when D > 0 -> 42 | generator(F, [1], D, 0). 43 | % 44 | generator(_, P, D, D) -> 45 | P; 46 | generator(F, P, D, Count) -> 47 | P0 = gf256:polynomial_product(F, P, [1, gf256:exponent(F, Count)]), 48 | generator(F, P0, D, Count + 1). 49 | 50 | % 51 | bch_code(Byte, Poly, MSB) -> 52 | case msb(Byte) >= MSB of 53 | true -> 54 | Byte0 = Byte bxor (Poly bsl (msb(Byte) - MSB)), 55 | bch_code(Byte0, Poly, MSB); 56 | false -> 57 | Byte 58 | end. 59 | 60 | %% 61 | msb(0) -> 62 | 0; 63 | msb(Byte) -> 64 | msb(Byte, 0). 65 | msb(0, Count) -> 66 | Count; 67 | msb(Byte, Count) -> 68 | msb(Byte bsr 1, Count + 1). 69 | 70 | -------------------------------------------------------------------------------- /src/base32.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | 15 | -module(base32). 16 | 17 | -export([encode/1, decode/1]). 18 | 19 | -define(BASE32_ALPHABET, { 20 | $A, $B, $C, $D, $E, $F, $G, $H, 21 | $I, $J, $K, $L, $M, $N, $O, $P, 22 | $Q, $R, $S, $T, $U, $V, $W, $X, 23 | $Y, $Z, $2, $3, $4, $5, $6, $7 24 | }). 25 | 26 | %% RFC 4648 27 | 28 | %% 29 | encode(Bin) when is_binary(Bin) -> 30 | Split = 5 * (byte_size(Bin) div 5), 31 | <> = Bin, 32 | Main = << <<(b32e(C))>> || <> <= Main0 >>, 33 | encode0(Rest, Main). 34 | 35 | encode0(<<>>, Acc) -> 36 | Acc; 37 | encode0(<>, Acc) -> 38 | <>; 39 | encode0(<>, Acc) -> 40 | <>; 41 | encode0(<>, Acc) -> 42 | <>; 43 | encode0(<>, Acc) -> 44 | <>, Acc) -> 53 | Bits = decode0(X) bsr 2, 54 | <>; 55 | decode(<>, Acc) -> 56 | Bits = decode0(X) bsr 4, 57 | <>; 58 | decode(<>, Acc) -> 59 | Bits = decode0(X) bsr 1, 60 | <>; 61 | decode(< 27 | %% Android: 28 | 29 | %% Google Authenticator URL Specification 30 | % @ref 31 | % otpauth://TYPE/LABEL?PARAMETERS 32 | % TYPE: hotp | totp 33 | % LABEL: string() (usually email address) 34 | % PARAMETERS: 35 | % digits = 6 | 8 (default 6) 36 | % counter = integer() (hotp only, default 0?) 37 | % period = integer() (in seconds, totp only, default 30) 38 | % secret = binary() base32 encoded 39 | % algorithm = MD5 | SHA1 | SHA256 | SHA512 (default SHA1) 40 | 41 | 42 | -include("qrcode.hrl"). 43 | 44 | -compile(export_all). 45 | 46 | -define(TTY(Term), io:format(user, "[~p] ~p~n", [?MODULE, Term])). 47 | -define(PERIOD, 30). 48 | 49 | run() -> 50 | Passcode = crypto:hash(sha, <<"password">>), 51 | run(<<"demo@mydomain.com">>, Passcode, ?PERIOD). 52 | 53 | run(Domain, Passcode, Seconds) -> 54 | PasscodeBase32 = base32:encode(Passcode), 55 | Period = list_to_binary(integer_to_list(Seconds)), 56 | Token = <<"otpauth://totp/", Domain/binary, "?period=", Period/binary, "&secret=", PasscodeBase32/binary>>, 57 | ?TTY({token, Token}), 58 | QRCode = qrcode:encode(Token), 59 | Image = simple_png_encode(QRCode), 60 | Filename = "qrcode.png", 61 | ok = file:write_file(Filename, Image), 62 | ?TTY({image, filename:absname(Filename)}), 63 | QRCode. 64 | 65 | %% Very simple PNG encoder for demo purposes 66 | simple_png_encode(#qrcode{dimension = Dim, data = Data}) -> 67 | MAGIC = <<137, 80, 78, 71, 13, 10, 26, 10>>, 68 | Size = Dim * 8, 69 | IHDR = png_chunk(<<"IHDR">>, <>), 70 | PixelData = get_pixel_data(Dim, Data), 71 | IDAT = png_chunk(<<"IDAT">>, PixelData), 72 | IEND = png_chunk(<<"IEND">>, <<>>), 73 | <>. 74 | 75 | png_chunk(Type, Bin) -> 76 | Length = byte_size(Bin), 77 | CRC = erlang:crc32(<>), 78 | <>. 79 | 80 | get_pixel_data(Dim, Data) -> 81 | Pixels = get_pixels(Data, 0, Dim, <<>>), 82 | zlib:compress(Pixels). 83 | 84 | get_pixels(<<>>, Dim, Dim, Acc) -> 85 | Acc; 86 | get_pixels(Bin, Count, Dim, Acc) -> 87 | <> = Bin, 88 | Row = get_pixels0(RowBits, <<0>>), % row filter byte 89 | FullRow = binary:copy(Row, 8), 90 | get_pixels(Bits, Count + 1, Dim, <>). 91 | 92 | get_pixels0(<<1:1, Bits/bits>>, Acc) -> 93 | Black = binary:copy(<<0>>, 24), 94 | get_pixels0(Bits, <>); 95 | get_pixels0(<<0:1, Bits/bits>>, Acc) -> 96 | White = binary:copy(<<255>>, 24), 97 | get_pixels0(Bits, <>); 98 | get_pixels0(<<>>, Acc) -> 99 | Acc. 100 | 101 | %% 102 | totp() -> 103 | Key = crypto:hash(sha, <<"password">>), 104 | totp(Key, ?PERIOD). 105 | totp(Key, Period) -> 106 | T = unow() div Period, 107 | {hotp(Key, T - 1), hotp(Key, T), hotp(Key, T + 1)}. 108 | %% RFC-4226 "HOTP: An HMAC-Based One-Time Password Algorithm" 109 | %% @ref 110 | hotp(Key, Count) when is_binary(Key), is_integer(Count) -> 111 | HS = crypto:hmac(sha, Key, <>), 112 | <<_:19/binary, _:4, Offset:4>> = HS, 113 | <<_:Offset/binary, _:1, P:31, _/binary>> = HS, 114 | HOTP = integer_to_list(P rem 1000000), 115 | Pad = lists:duplicate(6 - length(HOTP), $0), 116 | list_to_binary([Pad, HOTP]). 117 | 118 | 119 | -define(UNIX_TIME_ZERO, 62167219200). 120 | 121 | unow() -> 122 | calendar:datetime_to_gregorian_seconds(calendar:universal_time()) - ?UNIX_TIME_ZERO. 123 | -------------------------------------------------------------------------------- /src/gf256.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | 15 | %% NOTE: This module implements finite field arithmetic over the galois field 16 | % GF(256) with a specified prime modulus. 17 | 18 | -module(gf256). 19 | 20 | -export([field/1, add/3, subtract/3, multiply/3]). 21 | -export([exponent/2, log/2, inverse/2, value/3]). 22 | -export([monomial_product/4, polynomial_product/3, divide/3]). 23 | 24 | -record(gf256, {exponent, log}). 25 | 26 | % UNUSED 27 | %-record(gf256poly, {field, coefficients}). 28 | % NOTE: Implementation and use are greatly simplified by expressing polynomials 29 | % simply as lists of coefficient values, rather than explicit reification of 30 | % polynomial "objects". 31 | 32 | -define(RANGE, 255). 33 | 34 | %% 35 | field(PrimeModulus) -> 36 | Exponent = exponent_table(1, PrimeModulus, []), 37 | Log = log_table(Exponent, 1, [0]), 38 | #gf256{exponent = Exponent, log = Log}. 39 | % 40 | exponent_table(X, Modulus, Acc) when length(Acc) =< ?RANGE -> 41 | case X bsl 1 of 42 | V when V > ?RANGE -> 43 | X0 = V bxor Modulus; 44 | V -> 45 | X0 = V 46 | end, 47 | exponent_table(X0, Modulus, [X|Acc]); 48 | exponent_table(_, _, Acc) -> 49 | lists:reverse(Acc). 50 | % 51 | log_table(E, Count, Acc) when Count =< ?RANGE -> 52 | X = index_of(Count, 0, E), 53 | log_table(E, Count + 1, [X|Acc]); 54 | log_table(_, _, Acc) -> 55 | lists:reverse(Acc). 56 | % 57 | index_of(X, Count, [X|_]) -> 58 | Count; 59 | index_of(X, Count, [_|T]) -> 60 | index_of(X, Count + 1, T). 61 | 62 | %% 63 | add(#gf256{}, A, B) when is_integer(A), is_integer(B) -> 64 | A bxor B; 65 | add(#gf256{}, [0], B) when is_list(B) -> 66 | B; 67 | add(#gf256{}, A, [0]) when is_list(A) -> 68 | A; 69 | add(F = #gf256{}, A, B) when is_list(A), is_list(B) -> 70 | add(F, lists:reverse(A), lists:reverse(B), []). 71 | 72 | add(F, [H|T], [H0|T0], Acc) -> 73 | add(F, T, T0, [H bxor H0 | Acc]); 74 | add(F, [H|T], [], Acc) -> 75 | add(F, T, [], [H|Acc]); 76 | add(F, [], [H|T], Acc) -> 77 | add(F, [], T, [H|Acc]); 78 | add(_, [], [], Acc) -> 79 | Acc. 80 | 81 | %% NOTE: Subtraction is the same as addition over a galois field. 82 | subtract(F = #gf256{}, A, B) -> 83 | add(F, A, B). 84 | 85 | %% 86 | multiply(#gf256{}, 0, _) -> 87 | 0; 88 | multiply(#gf256{}, _, 0) -> 89 | 0; 90 | multiply(F = #gf256{}, A, B) -> 91 | X = (log(F, A) + log(F, B)) rem ?RANGE, 92 | exponent(F, X). 93 | 94 | %% 95 | exponent(#gf256{exponent = E}, X) -> 96 | lists:nth(X + 1, E). 97 | 98 | %% 99 | log(#gf256{log = L}, X) -> 100 | lists:nth(X + 1, L). 101 | 102 | %% 103 | inverse(F = #gf256{}, X) -> 104 | exponent(F, ?RANGE - log(F, X)). 105 | 106 | %% 107 | value(#gf256{}, Poly, 0) -> 108 | lists:last(Poly); 109 | value(F = #gf256{}, Poly, 1) -> 110 | lists:foldl(fun(X, Sum) -> gf256:add(F, X, Sum) end, 0, Poly); 111 | value(F = #gf256{}, [H|T], X) -> 112 | value(F, T, X, H). 113 | % 114 | value(F, [H|T], X, Acc) -> 115 | Acc0 = multiply(F, X, Acc), 116 | Acc1 = add(F, Acc0, H), 117 | value(F, T, X, Acc1); 118 | value(_, [], _, Acc) -> 119 | Acc. 120 | 121 | % 122 | monomial(#gf256{}, 0, Degree) when Degree >= 0 -> 123 | [0]; 124 | monomial(#gf256{}, Coeff, Degree) when Degree >= 0 -> 125 | [Coeff|lists:duplicate(Degree, 0)]. 126 | 127 | %% 128 | monomial_product(F, Poly, Coeff, Degree) -> 129 | monomial_product(F, Poly, Coeff, Degree, []). 130 | % 131 | monomial_product(F, [H|T], C, D, Acc) -> 132 | P = gf256:multiply(F, H, C), 133 | monomial_product(F, T, C, D, [P|Acc]); 134 | monomial_product(F, [], C, D, Acc) when D > 0 -> 135 | monomial_product(F, [], C, D - 1, [0|Acc]); 136 | monomial_product(_, [], _, 0, Acc) -> 137 | lists:reverse(Acc). 138 | 139 | %% 140 | polynomial_product(_, [0], _) -> 141 | [0]; 142 | polynomial_product(_, _, [0]) -> 143 | [0]; 144 | polynomial_product(F, P0, P1) -> 145 | polynomial_product0(F, P0, P1, [], []). 146 | % 147 | polynomial_product0(F, [H|T], P1, P2, Acc) -> 148 | [H0|T0] = polynomial_product1(F, H, P1, P2, []), 149 | polynomial_product0(F, T, P1, T0, [H0|Acc]); 150 | polynomial_product0(F, [], P1, [H|T], Acc) -> 151 | polynomial_product0(F, [], P1, T, [H|Acc]); 152 | polynomial_product0(_, [], _, [], Acc) -> 153 | lists:reverse(Acc). 154 | % 155 | polynomial_product1(_, _, [], [], Acc) -> 156 | lists:reverse(Acc); 157 | polynomial_product1(F, X, [H|T], [], Acc) -> 158 | Coeff = polynomial_product2(F, X, H, 0), 159 | polynomial_product1(F, X, T, [], [Coeff|Acc]); 160 | polynomial_product1(F, X, [H|T], [H0|T0], Acc) -> 161 | Coeff = polynomial_product2(F, X, H, H0), 162 | polynomial_product1(F, X, T, T0, [Coeff|Acc]). 163 | 164 | polynomial_product2(F, X, H, H0) -> 165 | Coeff = multiply(F, X, H), 166 | add(F, H0, Coeff). 167 | 168 | %% 169 | divide(F = #gf256{}, A, B = [H|_]) when B =/= [0] -> 170 | IDLT = inverse(F, H), 171 | divide(F, IDLT, B, [0], A). 172 | % 173 | divide(F, IDLT, B, Q, R = [H|_]) when length(R) >= length(B), R =/= [0] -> 174 | Diff = length(R) - length(B), 175 | Scale = multiply(F, H, IDLT), 176 | M = monomial(F, Scale, Diff), 177 | Q0 = add(F, Q, M), 178 | Coeffs = monomial_product(F, B, Scale, Diff), 179 | [_|R0] = add(F, R, Coeffs), 180 | divide(F, IDLT, B, Q0, R0); 181 | divide(_, _, _, Q, R) -> 182 | {Q, R}. 183 | -------------------------------------------------------------------------------- /src/qrcode.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | 15 | -module(qrcode). 16 | 17 | -include("qrcode.hrl"). 18 | -include("qrcode_params.hrl"). 19 | 20 | -export([encode/1, encode/2, decode/1]). 21 | 22 | %% 23 | decode(_Bin) -> 24 | {error, not_implemented}. 25 | 26 | %% 27 | encode(Bin) -> 28 | encode(Bin, 'M'). 29 | % 30 | encode(Bin, ECC) when is_binary(Bin) -> 31 | Params = choose_qr_params(Bin, ECC), 32 | Content = encode_content(Params, Bin), 33 | BlocksWithECC = generate_ecc_blocks(Params, Content), 34 | Codewords = interleave_blocks(BlocksWithECC), 35 | Matrix = qrcode_matrix:embed_data(Params, Codewords), 36 | MaskedMatrices = qrcode_mask:generate(Params, Matrix), 37 | Candidates = [qrcode_matrix:overlay_static(Params, M) || M <- MaskedMatrices], 38 | {MaskType, SelectedMatrix} = qrcode_mask:select(Candidates), 39 | Params0 = Params#qr_params{mask = MaskType}, 40 | FMT = format_info_bits(Params0), 41 | VSN = version_info_bits(Params0), 42 | #qr_params{version = Version, dimension = Dim, ec_level = _ECC} = Params0, 43 | QRCode = qrcode_matrix:finalize(Dim, FMT, VSN, ?QUIET_ZONE, SelectedMatrix), 44 | %% NOTE: Added "API" record 45 | #qrcode{version = Version, ecc = ECC, dimension = Dim + ?QUIET_ZONE * 2, data = QRCode}. 46 | 47 | %% 48 | choose_qr_params(Bin, ECLevel) -> 49 | Mode = choose_encoding(Bin), 50 | {Mode, Version, ECCBlockDefs, Remainder} = choose_version(Mode, ECLevel, byte_size(Bin)), 51 | AlignmentCoords = alignment_patterns(Version), 52 | Dim = qrcode_matrix:dimension(Version), 53 | #qr_params{mode = Mode, version = Version, dimension = Dim, ec_level = ECLevel, 54 | block_defs = ECCBlockDefs, align_coords = AlignmentCoords, remainder = Remainder, data = Bin}. 55 | 56 | %% NOTE: byte mode only (others removed) 57 | choose_encoding(_Bin) -> 58 | byte. 59 | 60 | %% 61 | choose_version(Type, ECC, Length) -> 62 | choose_version(Type, ECC, Length, ?TABLES). 63 | % 64 | choose_version(byte, ECC, Length, [{{ECC, Version}, {_, _, Capacity, _}, ECCBlocks, Remainder}|_]) 65 | when Capacity >= Length -> 66 | {byte, Version, ECCBlocks, Remainder}; 67 | choose_version(Type, ECC, Length, [_|T]) -> 68 | choose_version(Type, ECC, Length, T). 69 | 70 | %% 71 | encode_content(#qr_params{mode = Mode, version = Version}, Bin) -> 72 | encode_content(Mode, Version, Bin). 73 | % 74 | encode_content(byte, Version, Bin) -> 75 | encode_bytes(Version, Bin). 76 | 77 | %% 78 | generate_ecc_blocks(#qr_params{block_defs = ECCBlockDefs}, Bin) -> 79 | Bin0 = pad_data(Bin, ECCBlockDefs), 80 | generate_ecc(Bin0, ECCBlockDefs, []). 81 | 82 | 83 | pad_data(Bin, ECCBlockDefs) -> 84 | DataSize = byte_size(Bin), 85 | TotalSize = get_ecc_size(ECCBlockDefs), 86 | PaddingSize = TotalSize - DataSize, 87 | Padding = binary:copy(<>, PaddingSize bsr 1), 88 | case PaddingSize band 1 of 89 | 0 -> 90 | <>; 91 | 1 -> 92 | <> 93 | end. 94 | 95 | 96 | get_ecc_size(ECCBlockDefs) -> 97 | get_ecc_size(ECCBlockDefs, 0). 98 | get_ecc_size([{C, _, D}|T], Acc) -> 99 | get_ecc_size(T, C * D + Acc); 100 | get_ecc_size([], Acc) -> 101 | Acc. 102 | 103 | 104 | generate_ecc(Bin, [{C, L, D}|T], Acc) -> 105 | {Result, Bin0} = generate_ecc0(Bin, C, L, D, []), 106 | generate_ecc(Bin0, T, [Result|Acc]); 107 | generate_ecc(<<>>, [], Acc) -> 108 | lists:flatten(lists:reverse(Acc)). 109 | 110 | 111 | generate_ecc0(Bin, Count, TotalLength, BlockLength, Acc) when byte_size(Bin) >= BlockLength, Count > 0 -> 112 | <> = Bin, 113 | EC = qrcode_reedsolomon:encode(Block, TotalLength - BlockLength), 114 | generate_ecc0(Bin0, Count - 1, TotalLength, BlockLength, [{Block, EC}|Acc]); 115 | generate_ecc0(Bin, 0, _, _, Acc) -> 116 | {lists:reverse(Acc), Bin}. 117 | 118 | %% 119 | interleave_blocks(Blocks) -> 120 | Data = interleave_data(Blocks, <<>>), 121 | interleave_ecc(Blocks, Data). 122 | 123 | interleave_data(Blocks, Bin) -> 124 | Data = [X || {X, _} <- Blocks], 125 | interleave_blocks(Data, [], Bin). 126 | 127 | interleave_ecc(Blocks, Bin) -> 128 | Data = [X || {_, X} <- Blocks], 129 | interleave_blocks(Data, [], Bin). 130 | 131 | interleave_blocks([], [], Bin) -> 132 | Bin; 133 | interleave_blocks([], Acc, Bin) -> 134 | Acc0 = [X || X <- Acc, X =/= <<>>], 135 | interleave_blocks(lists:reverse(Acc0), [], Bin); 136 | interleave_blocks([<>|T], Acc, Bin) -> 137 | interleave_blocks(T, [Data|Acc], <>). 138 | 139 | % 140 | encode_bytes(Version, Bin) when is_binary(Bin) -> 141 | Size = size(Bin), 142 | CharacterCountBitSize = cci(?BYTE_MODE, Version), 143 | <>. 144 | 145 | 146 | %% Table 25. Error correction level indicators 147 | ecc('L') -> 1; 148 | ecc('M') -> 0; 149 | ecc('Q') -> 3; 150 | ecc('H') -> 2. 151 | 152 | % Table 5. Charset encoder 153 | % NOTE: removed 154 | 155 | %% 156 | alignment_patterns(Version) -> 157 | D = qrcode_matrix:dimension(Version), 158 | L = element(Version, ?ALIGNMENT_COORDINATES), 159 | L0 = [{X, Y} || X <- L, Y <- L], 160 | L1 = [{X, Y} || {X, Y} <- L0, is_finder_region(D, X, Y) =:= false], 161 | % Change the natural sort order so that rows have greater weight than columns 162 | F = fun 163 | ({_, Y}, {_, Y0}) when Y < Y0 -> 164 | true; 165 | ({X, Y}, {X0, Y0}) when Y =:= Y0 andalso X =< X0 -> 166 | true; 167 | (_, _) -> 168 | false 169 | end, 170 | lists:sort(F, L1). 171 | % 172 | is_finder_region(D, X, Y) 173 | when (X =< 8 andalso Y =< 8) 174 | orelse (X =< 8 andalso Y >= D - 8) 175 | orelse (X >= D - 8 andalso Y =< 8) -> 176 | true; 177 | is_finder_region(_, _, _) -> 178 | false. 179 | 180 | %% Table 3. Number of bits in Character Count Indicator 181 | cci(Mode, Version) when Version >= 1 andalso Version =< 40-> 182 | {Mode, CC} = lists:keyfind(Mode, 1, ?CCI_BITSIZE), 183 | cci0(CC, Version). 184 | % 185 | cci0([X, _, _], Version) when Version =< 9 -> 186 | X; 187 | cci0([_, X, _], Version) when Version =< 26 -> 188 | X; 189 | cci0([_, _, X], _) -> 190 | X. 191 | 192 | version_info_bits(#qr_params{version = Version}) when Version < 7 -> 193 | <<>>; 194 | version_info_bits(#qr_params{version = Version}) when Version =< 40 -> 195 | BCH = qrcode_reedsolomon:bch_code(Version, ?VERSION_INFO_POLY), 196 | <>. 197 | 198 | format_info_bits(#qr_params{ec_level = ECLevel, mask = MaskType}) -> 199 | Info = (ecc(ECLevel) bsl 3) bor MaskType, 200 | BCH = qrcode_reedsolomon:bch_code(Info, ?FORMAT_INFO_POLY), 201 | InfoWithEC = (Info bsl 10) bor BCH, 202 | Value = InfoWithEC bxor ?FORMAT_INFO_MASK, 203 | <>. 204 | -------------------------------------------------------------------------------- /src/qrcode_mask.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | 15 | -module(qrcode_mask). 16 | 17 | -include("qrcode_params.hrl"). 18 | 19 | -export([generate/2, select/1]). 20 | 21 | -define(PENALTY_RULE_1, 3). 22 | -define(PENALTY_RULE_2, 3). 23 | -define(PENALTY_RULE_3, 40). 24 | -define(PENALTY_RULE_4, 10). 25 | 26 | %% Generates all eight masked versions of the bit matrix 27 | generate(#qr_params{dimension = Dim}, Matrix) -> 28 | Sequence = lists:seq(0, 7), 29 | Functions = [mask(X) || X <- Sequence], 30 | Masks = [generate_mask(Dim, MF) || MF <- Functions], 31 | [apply_mask(Matrix, Mask, []) || Mask <- Masks]. 32 | 33 | %% Selects the lowest penalty candidate from a list of bit matrices 34 | select([H|T]) -> 35 | Score = score_candidate(H), 36 | select_candidate(T, 0, 0, Score, H). 37 | 38 | %% Internal 39 | 40 | % 41 | generate_mask(Max, MF) -> 42 | Sequence = lists:seq(0, Max - 1), 43 | [generate_mask(Sequence, Y, MF) || Y <- Sequence]. 44 | generate_mask(Sequence, Y, MF) -> 45 | [case MF(X, Y) of true -> 1; false -> 0 end || X <- Sequence]. 46 | 47 | apply_mask([H|T], [H0|T0], Acc) -> 48 | Row = apply_mask0(H, H0, []), 49 | apply_mask(T, T0, [Row|Acc]); 50 | apply_mask([], [], Acc) -> 51 | lists:reverse(Acc). 52 | 53 | apply_mask0([H|T], [H0|T0], Acc) when is_integer(H) -> 54 | apply_mask0(T, T0, [H bxor H0|Acc]); 55 | apply_mask0([H|T], [_|T0], Acc) -> 56 | apply_mask0(T, T0, [H|Acc]); 57 | apply_mask0([], [], Acc) -> 58 | lists:reverse(Acc). 59 | 60 | % (i + j) mod 2 = 0 61 | mask(0) -> 62 | fun(X, Y) -> (X + Y) rem 2 =:= 0 end; 63 | % i mod 2 = 0 64 | mask(1) -> 65 | fun(_X, Y) -> Y rem 2 =:= 0 end; 66 | % j mod 3 = 0 67 | mask(2) -> 68 | fun(X, _Y) -> X rem 3 =:= 0 end; 69 | % (i + j) mod 3 = 0 70 | mask(3) -> 71 | fun(X, Y) -> (X + Y) rem 3 =:= 0 end; 72 | % ((i div 2) + (j div 3)) mod 2 = 0 73 | mask(4) -> 74 | fun(X, Y) -> (X div 3 + Y div 2) rem 2 =:= 0 end; 75 | %101 (i * j) mod 2 + (i *j) mod 3 = 0 76 | mask(5) -> 77 | fun(X, Y) -> Sum = X * Y, Sum rem 2 + Sum rem 3 =:= 0 end; 78 | % ((i * j) mod 2 + (i* j) mod 3) mod 2 = 0 79 | mask(6) -> 80 | fun(X, Y) -> Sum = X * Y, (Sum rem 2 + Sum rem 3) rem 2 =:= 0 end; 81 | %((i * j) mod 3 + (i + j) mod 2) mod 2 = 0 82 | mask(7) -> 83 | fun(X, Y) -> ((X * Y rem 3) + ((X + Y) rem 2)) rem 2 =:= 0 end. 84 | 85 | select_candidate([H|T], Count, Mask, Score, C) -> 86 | case score_candidate(H) of 87 | X when X < Score -> 88 | select_candidate(T, Count + 1, Count + 1, X, H); 89 | _ -> 90 | select_candidate(T, Count + 1, Mask, Score, C) 91 | end; 92 | select_candidate([], _, Mask, _Score, C) -> 93 | %?TTY({selected, Mask, {score, Score}}), 94 | {Mask, C}. 95 | 96 | score_candidate(C) -> 97 | Rule1 = apply_penalty_rule_1(C), 98 | Rule2 = apply_penalty_rule_2(C), 99 | Rule3 = apply_penalty_rule_3(C), 100 | Rule4 = apply_penalty_rule_4(C), 101 | Total = Rule1 + Rule2 + Rule3 + Rule4, 102 | %?TTY({score, Total, [Rule1, Rule2, Rule3, Rule4]}), 103 | Total. 104 | 105 | %% Section 8.2.2 106 | apply_penalty_rule_1(Candidate) -> 107 | ScoreRows = rule1(Candidate, 0), 108 | ScoreCols = rule1(rows_to_columns(Candidate), 0), 109 | ScoreRows + ScoreCols. 110 | % 111 | rule1([Row|T], Score) -> 112 | Score0 = rule1_row(Row, Score), 113 | rule1(T, Score0); 114 | rule1([], Score) -> 115 | Score. 116 | % 117 | rule1_row(L = [H|_], Score) -> 118 | F = fun 119 | (1) when H =:= 1 -> 120 | true; 121 | (1) -> 122 | false; 123 | (_) when H =:= 0 orelse is_integer(H) =:= false -> 124 | true; 125 | (_) -> 126 | false 127 | end, 128 | {H0,T0} = lists:splitwith(F, L), 129 | case length(H0) of 130 | Repeats when Repeats >= 5 -> 131 | Penalty = ?PENALTY_RULE_1 + Repeats - 5, 132 | rule1_row(T0, Score + Penalty); 133 | _ -> 134 | rule1_row(T0, Score) 135 | end; 136 | rule1_row([], Score) -> 137 | Score. 138 | 139 | %% 140 | apply_penalty_rule_2(_M = [H, H0|T]) -> 141 | % ?TTY(M), 142 | Blocks = rule2(1, 1, H, H0, [H0|T], []), 143 | Blocks0 = composite_blocks(Blocks, []), 144 | Blocks1 = composite_blocks(Blocks0, []), 145 | % ?TTY(Blocks1), 146 | score_blocks(Blocks1, 0). 147 | 148 | score_blocks([{_, {M, N}, _}|T], Acc) -> 149 | Score = ?PENALTY_RULE_2 * (M - 1) * (N - 1), 150 | score_blocks(T, Acc + Score); 151 | score_blocks([], Acc) -> 152 | Acc. 153 | 154 | rule2(X, Y, [H, H|T], [H, H|T0], Rows, Acc) -> 155 | rule2(X + 1, Y, [H|T], [H|T0], Rows, [{{X, Y}, {2, 2}, H}|Acc]); 156 | rule2(X, Y, [_|T], [_|T0], Rows, Acc) -> 157 | rule2(X + 1, Y, T, T0, Rows, Acc); 158 | rule2(_, Y, [], [], [H, H0|T], Acc) -> 159 | rule2(1, Y + 1, H, H0, [H0|T], Acc); 160 | rule2(_, _, [], [], [_], Acc) -> 161 | lists:reverse(Acc). 162 | 163 | composite_blocks([H|T], Acc) -> 164 | {H0, T0} = composite_block(H, T, []), 165 | composite_blocks(T0, [H0|Acc]); 166 | composite_blocks([], Acc) -> 167 | lists:reverse(Acc). 168 | 169 | composite_block(B, [H|T], Acc) -> 170 | case combine_block(B, H) of 171 | false -> 172 | composite_block(B, T, [H|Acc]); 173 | B0 -> 174 | composite_block(B0, T, Acc) 175 | end; 176 | composite_block(B, [], Acc) -> 177 | {B, lists:reverse(Acc)}. 178 | 179 | % Does Block 0 contain the Block 1 coordinate? 180 | combine_block(B = {{X, Y}, {SX, SY}, _}, B0 = {{X0, Y0}, _, _}) 181 | when X0 < X + SX orelse Y0 < Y + SY -> 182 | combine_block0(B, B0); 183 | combine_block(_, _) -> 184 | false. 185 | 186 | % are they same valued? 187 | combine_block0(B = {_, _, V}, B0 = {_, _, V0}) 188 | when V =:= V0 orelse (V =/= 1 andalso V0 =/= 1) -> 189 | combine_block1(B, B0); 190 | combine_block0(_, _) -> 191 | false. 192 | 193 | % is B extended by B0 horizontally? 194 | combine_block1({{X, Y}, {SX, SY}, V}, {{X0, Y}, {SX0, SY}, _}) when X0 =:= X + SX - 1 -> 195 | {{X, Y}, {SX + SX0 - 1, SY}, V}; 196 | % is B extended by B0 vertically? 197 | combine_block1({{X, Y}, {SX, SY}, V}, {{X, Y0}, {SX, SY0}, _}) when Y0 =:= Y + SY - 1 -> 198 | {{X, Y}, {SX, SY + SY0 - 1}, V}; 199 | combine_block1(_, _) -> 200 | false. 201 | 202 | %% 203 | apply_penalty_rule_3(Candidate) -> 204 | RowScores = [rule3(Row, 0) || Row <- Candidate], 205 | ColumnScores = [rule3(Col, 0) || Col <- rows_to_columns(Candidate)], 206 | lists:sum(RowScores) + lists:sum(ColumnScores). 207 | % 208 | rule3(Row = [1|T], Score) -> 209 | Ones = lists:takewhile(fun(X) -> X =:= 1 end, Row), 210 | Scale = length(Ones), 211 | case Scale * 7 of 212 | Length when Length > length(Row) -> 213 | rule3(T, Score); 214 | Length -> 215 | case is_11311_pattern(lists:sublist(Row, Length), Scale) of 216 | true -> 217 | rule3(T, Score + ?PENALTY_RULE_3); 218 | false -> 219 | rule3(T, Score) 220 | end 221 | end; 222 | rule3([_|T], Score) -> 223 | rule3(T, Score); 224 | rule3([], Acc) -> 225 | Acc. 226 | % 227 | is_11311_pattern(List, Scale) -> 228 | List0 = lists:map(fun(X) when X =:= 1 -> 1; (_) -> 0 end, List), 229 | Result = condense(List0, Scale, []), 230 | Result =:= [1,0,1,1,1,0,1]. 231 | % 232 | condense([], _, Acc) -> 233 | lists:reverse(Acc); 234 | condense(L, Scale, Acc) -> 235 | {H, T} = lists:split(Scale, L), 236 | case lists:sum(H) of 237 | Scale -> 238 | condense(T, Scale, [1|Acc]); 239 | 0 -> 240 | condense(T, Scale, [0|Acc]); 241 | _ -> 242 | undefined 243 | end. 244 | 245 | %% 246 | apply_penalty_rule_4(Candidate) -> 247 | Proportion = rule4(Candidate, 0, 0), 248 | %?TTY({proportion, Proportion}), 249 | ?PENALTY_RULE_4 * (trunc(abs(Proportion * 100 - 50)) div 5). 250 | % 251 | rule4([H|T], Dark, All) -> 252 | All0 = All + length(H), 253 | Dark0 = Dark + length([X || X <- H, X =:= 1]), 254 | rule4(T, Dark0, All0); 255 | rule4([], Dark, All) -> 256 | Dark / All. 257 | 258 | % 259 | rows_to_columns(L) -> 260 | rows_to_columns(L, []). 261 | rows_to_columns([[]|_], Acc) -> 262 | lists:reverse(Acc); 263 | rows_to_columns(L, Acc) -> 264 | Heads = [H || [H|_] <- L], 265 | Tails = [T || [_|T] <- L], 266 | rows_to_columns(Tails, [Heads|Acc]). 267 | 268 | -------------------------------------------------------------------------------- /src/qrcode_matrix.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | 15 | -module(qrcode_matrix). 16 | 17 | -include("qrcode_params.hrl"). 18 | 19 | -export([dimension/1, template/1, embed_data/2, overlay_static/2, finalize/5]). 20 | 21 | -define(FINDER_BITS, <<6240274796270654599595212063015969838585429452563217548030:192>>). 22 | 23 | %% 24 | dimension(Version) 25 | when Version > 0 26 | andalso Version < 41 -> 27 | 17 + (Version * 4). 28 | 29 | %% 30 | template(#qr_params{version = Version, align_coords = AC}) -> 31 | template(Version, AC). 32 | 33 | %% 34 | embed_data(#qr_params{version = Version, align_coords = AC, remainder = Rem}, Codewords) -> 35 | FlippedTemplate = flip(template(Version, AC)), 36 | FlippedMatrix = embed_data(FlippedTemplate, <>, []), 37 | flip(FlippedMatrix). 38 | 39 | %% 40 | overlay_static(#qr_params{version = Version, align_coords = AC}, Matrix) -> 41 | F = finder_bits(), 42 | T = timing_bits(Version, AC), 43 | A = alignment_bits(AC), 44 | overlay_static(Matrix, F, T, A, []). 45 | 46 | %% 47 | finalize(Dim, FMT, VSN, QZ, Matrix) -> 48 | M = format_bits(FMT), 49 | V = version_bits(VSN), 50 | FinalMatrix = overlay_format(Matrix, M, V, []), 51 | QBitLength = (Dim + QZ * 2) * QZ, 52 | Q = <<0:QBitLength>>, 53 | Bin = encode_bits(FinalMatrix, QZ, Q), 54 | <>. 55 | 56 | %% Internal 57 | 58 | %% 59 | template(Version, AC) -> 60 | Dim = dimension(Version), 61 | template(1, Dim, AC, []). 62 | % 63 | template(Y, Max, AC, Acc) when Y =< Max-> 64 | Row = template_row(1, Y, Max, AC, []), 65 | template(Y + 1, Max, AC, [Row|Acc]); 66 | template(_, _, _, Acc) -> 67 | lists:reverse(Acc). 68 | % 69 | template_row(X, Y, Max, AC, Acc) when X =< Max -> 70 | Ref = template_ref(X, Y, Max, AC), 71 | template_row(X + 1, Y, Max, AC, [Ref|Acc]); 72 | template_row(_, _, _, _, Acc) -> 73 | lists:reverse(Acc). 74 | % 75 | template_ref(X, Y, Max, _AC) 76 | when (X =< 8 andalso Y =< 8) 77 | orelse (X =< 8 andalso Y > Max - 8) 78 | orelse (X > Max - 8 andalso Y =< 8) -> 79 | f; 80 | template_ref(X, Y, Max, _AC) 81 | when (X =:= 9 andalso Y =/= 7 andalso (Y =< 9 orelse Max - Y =< 7)) 82 | orelse (Y =:= 9 andalso X =/= 7 andalso (X =< 9 orelse Max - X =< 7)) -> 83 | m; 84 | template_ref(X, Y, Max, _AC) 85 | when Max >= 45 86 | andalso ((X < 7 andalso Max - Y =< 10) 87 | orelse (Max - X =< 10 andalso Y < 7)) -> 88 | v; 89 | template_ref(X, Y, Max, AC) -> 90 | case is_alignment_bit(X, Y, AC) of 91 | true -> 92 | a; 93 | false -> 94 | template_ref0(X, Y, Max) 95 | end. 96 | % 97 | template_ref0(X, Y, _) 98 | when X =:= 7 99 | orelse Y =:= 7 -> 100 | t; 101 | template_ref0(_, _, _) -> 102 | d. 103 | 104 | %% 105 | is_alignment_bit(X, Y, [{Xa, Ya}|_]) 106 | when (X >= Xa - 2 107 | andalso X =< Xa + 2 108 | andalso Y >= Ya - 2 109 | andalso Y =< Ya + 2) -> 110 | true; 111 | is_alignment_bit(X, Y, [_|T]) -> 112 | is_alignment_bit(X, Y, T); 113 | is_alignment_bit(_X, _Y, []) -> 114 | false. 115 | 116 | % deal with row 7 exceptional case 117 | embed_data([HA, HB, H, HC, HD|T], Codewords, Acc) when length(T) =:= 4 -> % skip row 7 118 | {HA0, HB0, Codewords0} = embed_data(HA, HB, Codewords, [], []), 119 | {HC0, HD0, Codewords1} = embed_data_reversed(HC, HD, Codewords0), 120 | embed_data(T, Codewords1, [HD0, HC0, H, HB0, HA0|Acc]); 121 | % normal case 122 | embed_data([HA, HB, HC, HD|T], Codewords, Acc) -> 123 | {HA0, HB0, Codewords0} = embed_data(HA, HB, Codewords, [], []), 124 | {HC0, HD0, Codewords1} = embed_data_reversed(HC, HD, Codewords0), 125 | embed_data(T, Codewords1, [HD0, HC0, HB0, HA0|Acc]); 126 | embed_data([], <<>>, Acc) -> 127 | lists:reverse(Acc). 128 | 129 | embed_data([d|T0], [d|T1], <>, StreamA, StreamB) -> 130 | embed_data(T0, T1, Codewords, [A|StreamA], [B|StreamB]); 131 | embed_data([d|T0], [B|T1], <>, StreamA, StreamB) -> 132 | embed_data(T0, T1, Codewords, [A|StreamA], [B|StreamB]); 133 | embed_data([A|T0], [d|T1], <>, StreamA, StreamB) -> 134 | embed_data(T0, T1, Codewords, [A|StreamA], [B|StreamB]); 135 | embed_data([A|T0], [B|T1], Codewords, StreamA, StreamB) -> 136 | embed_data(T0, T1, Codewords, [A|StreamA], [B|StreamB]); 137 | embed_data([], [], Codewords, StreamA, StreamB) -> 138 | {lists:reverse(StreamA), lists:reverse(StreamB), Codewords}. 139 | 140 | embed_data_reversed(A, B, Codewords) -> 141 | {A0, B0, Codewords0} = embed_data(lists:reverse(A), lists:reverse(B), Codewords, [], []), 142 | {lists:reverse(A0), lists:reverse(B0), Codewords0}. 143 | 144 | % 145 | overlay_static([H|L], F, T, A, Acc) -> 146 | {F0, T0, A0, Row} = overlay0(H, F, T, A, []), 147 | overlay_static(L, F0, T0, A0, [Row|Acc]); 148 | overlay_static([], <<>>, <<>>, <<>>, Acc) -> 149 | lists:reverse(Acc). 150 | % 151 | overlay0([f|L], <>, T, A, Acc) -> 152 | overlay0(L, F, T, A, [F0|Acc]); 153 | overlay0([t|L], F, <>, A, Acc) -> 154 | overlay0(L, F, T, A, [T0|Acc]); 155 | overlay0([a|L], F, T, <>, Acc) -> 156 | overlay0(L, F, T, A, [A0|Acc]); 157 | overlay0([H|L], F, T, A, Acc) -> 158 | overlay0(L, F, T, A, [H|Acc]); 159 | overlay0([], F, T, A, Acc) -> 160 | {F, T, A, lists:reverse(Acc)}. 161 | 162 | % 163 | encode_bits([H|T], QZ, Acc) -> 164 | Acc0 = encode_bits0(H, <>), 165 | encode_bits(T, QZ, <>); 166 | encode_bits([], _, Acc) -> 167 | Acc. 168 | 169 | encode_bits0([H|T], Acc) when is_integer(H) -> 170 | encode_bits0(T, <>); 171 | encode_bits0([], Acc) -> 172 | Acc. 173 | % 174 | overlay_format([H|L], M, V, Acc) -> 175 | {M0, V0, Row} = overlay1(H, M, V, []), 176 | overlay_format(L, M0, V0, [Row|Acc]); 177 | overlay_format([], <<>>, <<>>, Acc) -> 178 | lists:reverse(Acc). 179 | % 180 | overlay1([m|L], <>, V, Acc) -> 181 | overlay1(L, M, V, [M0|Acc]); 182 | overlay1([v|L], M, <>, Acc) -> 183 | overlay1(L, M, V, [V0|Acc]); 184 | overlay1([H|L], M, V, Acc) -> 185 | overlay1(L, M, V, [H|Acc]); 186 | overlay1([], M, V, Acc) -> 187 | {M, V, lists:reverse(Acc)}. 188 | 189 | % 190 | flip(L) -> 191 | flip(L, []). 192 | flip([[]|T], Acc) -> 193 | [[] || [] <- T], % guard check 194 | [lists:reverse(L) || L <- Acc]; 195 | flip(L, Acc) -> 196 | Heads = [H || [H|_] <- L], 197 | Tails = [T || [_|T] <- L], 198 | flip(Tails, [Heads|Acc]). 199 | 200 | %% 201 | finder_bits() -> 202 | ?FINDER_BITS. 203 | %% 204 | alignment_bits(AC) -> 205 | Repeats = composite_ac(AC, []), 206 | alignment_bits(Repeats, <<>>). 207 | alignment_bits([H|T], Acc) -> 208 | Bits0 = bits:duplicate(<<31:5>>, H), 209 | Bits1 = bits:duplicate(<<17:5>>, H), 210 | Bits2 = bits:duplicate(<<21:5>>, H), 211 | Bits = bits:append([Bits0, Bits1, Bits2, Bits1, Bits0]), 212 | alignment_bits(T, <>); 213 | alignment_bits([], Acc) -> 214 | Acc. 215 | % 216 | composite_ac([{_, Row}|T], Acc) -> 217 | N = 1 + length([{X, Y} || {X, Y} <- T, Y =:= Row]), 218 | T0 = [{X, Y} || {X, Y} <- T, Y =/= Row], 219 | composite_ac(T0, [N|Acc]); 220 | composite_ac([], Acc) -> 221 | lists:reverse(Acc). 222 | 223 | %% 224 | timing_bits(Version, AC) -> 225 | Length = dimension(Version) - 16, 226 | % alignment pattern start coordinates, to trigger bit skipping 227 | TH = timing_bits(1, Length, [X - 8 - 2 || {X, 7} <- AC], <<>>), 228 | TV = timing_bits(1, Length, [Y - 8 - 2 || {7, Y} <- AC], <<>>), 229 | <>. 230 | % 231 | timing_bits(N, Max, A, Acc) when N =< Max -> 232 | case lists:member(N, A) of 233 | true -> % skip the alignment pattern 234 | timing_bits(N + 5, Max, A, Acc); 235 | false -> 236 | Bit = N band 1, 237 | timing_bits(N + 1, Max, A, <>) 238 | end; 239 | timing_bits(_, _, _, Acc) -> 240 | Acc. 241 | 242 | %% 243 | format_bits(Bin) -> 244 | <> = bits:reverse(Bin), 245 | <> = Bin, 246 | <>. 247 | 248 | %% 249 | version_bits(Bin) -> 250 | VTop = bits:reverse(Bin), 251 | VLeft = version_bits(VTop, []), 252 | <>. 253 | % 254 | version_bits(<>, Acc) -> 255 | version_bits(Bin, [X|Acc]); 256 | version_bits(<<>>, Acc) -> 257 | version_bits(lists:reverse(Acc), <<>>, <<>>, <<>>). 258 | % 259 | version_bits([<>|T], RowA, RowB, RowC) -> 260 | version_bits(T, <>, <>, <>); 261 | version_bits([], RowA, RowB, RowC) -> 262 | bits:append([RowA, RowB, RowC]). 263 | 264 | -------------------------------------------------------------------------------- /src/qrcode_params.hrl: -------------------------------------------------------------------------------- 1 | %% Copyright 2011 Steve Davis 2 | % 3 | % Licensed under the Apache License, Version 2.0 (the "License"); 4 | % you may not use this file except in compliance with the License. 5 | % You may obtain a copy of the License at 6 | % 7 | % http://www.apache.org/licenses/LICENSE-2.0 8 | % 9 | % Unless required by applicable law or agreed to in writing, software 10 | % distributed under the License is distributed on an "AS IS" BASIS, 11 | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | % See the License for the specific language governing permissions and 13 | % limitations under the License. 14 | % 15 | % qrcode_params.hrl 16 | 17 | -record(qr_params, {mode, version, dimension, ec_level, block_defs, align_coords, remainder, mask, data}). 18 | 19 | -define(QR_GF256_PRIME_MODULUS, 285). % 16#011D -> 2^8 + 2^4 + 2^3 + 2^2 + 1 20 | 21 | -define(VERSION_INFO_POLY, 7973). % 16#1f25 -> 0001 1111 0010 0101 22 | -define(FORMAT_INFO_POLY, 1335). % 16#0537 -> 0000 0101 0011 1110 23 | -define(FORMAT_INFO_MASK, 21522). % 16#5412 -> 0101 0100 0001 0010 24 | 25 | -define(QUIET_ZONE, 4). % recommended value 26 | 27 | %% Table 2. Mode Indicator 28 | -define(TERMINATOR, 0). 29 | -define(NUMERIC_MODE, 1). 30 | -define(ALPHANUMERIC_MODE, 2). 31 | -define(STRUCTURED_APPEND_MODE, 3). 32 | -define(FNC1_FIRST_POSITION_MODE, 5). 33 | -define(BYTE_MODE, 4). 34 | -define(ECI_MODE, 7). 35 | -define(KANJI_MODE, 8). 36 | -define(FNC1_SECOND_POSITION_MODE, 9). 37 | 38 | %% Table 3. Number of bits in Character Count Indicator 39 | %% {Mode, [v0-v9, v10-v26, v27-v40]} 40 | -define(CCI_BITSIZE, [ 41 | {?NUMERIC_MODE, [10, 12, 14]}, 42 | {?ALPHANUMERIC_MODE, [9, 11, 13]}, 43 | {?BYTE_MODE, [8, 16, 16]}, 44 | {?KANJI_MODE, [8, 16, 16]} 45 | ]). 46 | 47 | % Table 5. Alphanumeric charset - see also char/1 48 | -define(CHARSET, <<"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:">>). 49 | -define(ALPHANUMERIC_REGEX, <<"[", ?CHARSET/binary, "]+">>). 50 | -define(NUMERIC_REGEX, <<"[0123456789]+">>). 51 | 52 | % Section 8.4.9 53 | -define(DATA_PAD_0, 236). % 11101100 54 | -define(DATA_PAD_1, 17). % 00010001 55 | 56 | %% Annex E. Table E.1 - Origin at 1, 1 rather than 0, 0 57 | -define(ALIGNMENT_COORDINATES, { 58 | [], 59 | [7, 19], 60 | [7, 23], 61 | [7, 27], 62 | [7, 31], 63 | [7, 35], 64 | [7, 23, 39], % Version 7 65 | [7, 25, 43], 66 | [7, 27, 47], 67 | [7, 29, 51], 68 | [7, 31, 55], 69 | [7, 33, 59], 70 | [7, 35, 63], 71 | [7, 27, 47, 67], % Version 14 72 | [7, 27, 49, 71], 73 | [7, 27, 51, 75], 74 | [7, 31, 55, 79], 75 | [7, 31, 57, 83], 76 | [7, 31, 59, 87], 77 | [7, 35, 63, 91], 78 | [7, 29, 51, 73, 95], % Version 21 79 | [7, 27, 51, 75, 99], 80 | [7, 31, 55, 79, 103], 81 | [7, 29, 55, 81, 107], 82 | [7, 33, 59, 85, 111], 83 | [7, 31, 59, 87, 115], 84 | [7, 35, 63, 91, 119], 85 | [7, 27, 51, 75, 99, 123], % Version 28 86 | [7, 31, 55, 79, 103, 127], 87 | [7, 27, 53, 79, 105, 131], 88 | [7, 31, 57, 83, 109, 135], 89 | [7, 35, 61, 87, 113, 139], 90 | [7, 31, 59, 87, 115, 143], 91 | [7, 35, 63, 91, 119, 147], 92 | [7, 31, 55, 79, 103, 127, 151], % Version 35 93 | [7, 25, 51, 77, 103, 129, 155], 94 | [7, 29, 55, 81, 107, 133, 159], 95 | [7, 33, 59, 85, 111, 137, 163], 96 | [7, 27, 55, 83, 111, 139, 167], 97 | [7, 31, 59, 87, 115, 143, 171] % Version 40 98 | }). 99 | 100 | % Composite of Tables 1, 7-11, 13-22 101 | % {{level, version}, {numeric_capacity, alpha_capacity, byte_capacity, kanji_capacity}, ecc_blocks[{number_of_blocks, total_bytes, data_bytes], remainder_bits} 102 | -define(TABLES, [ 103 | {{'L',1},{41,25,17,10},[{1,26,19}],0}, 104 | {{'L',2},{77,47,32,20},[{1,44,34}],7}, 105 | {{'L',3},{127,77,53,32},[{1,70,55}],7}, 106 | {{'L',4},{187,114,78,48},[{1,100,80}],7}, 107 | {{'L',5},{255,154,106,65},[{1,134,108}],7}, 108 | {{'L',6},{322,195,134,82},[{2,86,68}],7}, 109 | {{'L',7},{370,224,154,95},[{2,98,78}],0}, 110 | {{'L',8},{461,279,192,118},[{2,121,97}],0}, 111 | {{'L',9},{552,335,230,141},[{2,146,116}],0}, 112 | {{'L',10},{652,395,271,167},[{2,86,68},{2,87,69}],0}, 113 | {{'L',11},{772,468,321,198},[{4,101,81}],0}, 114 | {{'L',12},{883,535,367,226},[{2,116,92},{2,117,93}],0}, 115 | {{'L',13},{1022,619,425,262},[{4,133,107}],0}, 116 | {{'L',14},{1101,667,458,282},[{3,145,115},{1,146,116}],3}, 117 | {{'L',15},{1250,758,520,320},[{5,109,87},{1,110,88}],3}, 118 | {{'L',16},{1408,854,586,361},[{5,122,98},{1,123,99}],3}, 119 | {{'L',17},{1548,938,644,397},[{1,135,107},{5,136,108}],3}, 120 | {{'L',18},{1725,1046,718,442},[{5,150,120},{1,151,121}],3}, 121 | {{'L',19},{1903,1153,792,488},[{3,141,113},{4,142,114}],3}, 122 | {{'L',20},{2061,1249,858,528},[{3,135,107},{5,136,108}],3}, 123 | {{'L',21},{2232,1352,929,572},[{4,144,116},{4,145,117}],4}, 124 | {{'L',22},{2409,1460,1003,618},[{2,139,111},{7,140,112}],4}, 125 | {{'L',23},{2620,1588,1091,672},[{4,151,121},{5,152,122}],4}, 126 | {{'L',24},{2812,1704,1171,721},[{6,147,117},{4,148,118}],4}, 127 | {{'L',25},{3057,1853,1273,784},[{8,132,106},{4,133,107}],4}, 128 | {{'L',26},{3283,1990,1367,842},[{10,142,114},{2,143,115}],4}, 129 | {{'L',27},{3517,2132,1465,902},[{8,152,122},{4,153,123}],4}, 130 | {{'L',28},{3669,2223,1528,940},[{3,147,117},{10,148,118}],3}, 131 | {{'L',29},{3909,2369,1628,1002},[{7,146,116},{7,147,117}],3}, 132 | {{'L',30},{4158,2520,1732,1066},[{5,145,115},{10,146,116}],3}, 133 | {{'L',31},{4417,2677,1840,1132},[{13,145,115},{3,146,116}],3}, 134 | {{'L',32},{4686,2840,1952,1201},[{17,145,115}],3}, 135 | {{'L',33},{4965,3009,2068,1273},[{17,145,115},{1,146,116}],3}, 136 | {{'L',34},{5253,3183,2188,1347},[{13,145,115},{6,146,116}],3}, 137 | {{'L',35},{5529,3351,2303,1417},[{12,151,121},{7,152,122}],0}, 138 | {{'L',36},{5836,3537,2431,1496},[{6,151,121},{14,152,122}],0}, 139 | {{'L',37},{6153,3729,2563,1577},[{17,152,122},{4,153,123}],0}, 140 | {{'L',38},{6479,3927,2699,1661},[{4,152,122},{18,153,123}],0}, 141 | {{'L',39},{6743,4087,2809,1729},[{20,147,117},{4,148,118}],0}, 142 | {{'L',40},{7089,4296,2953,1817},[{19,148,118},{6,149,119}],0}, 143 | {{'M',1},{34,20,14,8},[{1,26,16}],0}, 144 | {{'M',2},{63,38,26,16},[{1,44,28}],7}, 145 | {{'M',3},{101,61,42,26},[{1,70,44}],7}, 146 | {{'M',4},{149,90,62,38},[{2,50,32}],7}, 147 | {{'M',5},{202,122,84,52},[{2,67,43}],7}, 148 | {{'M',6},{255,154,106,65},[{4,43,27}],7}, 149 | {{'M',7},{293,178,122,75},[{4,49,31}],0}, 150 | {{'M',8},{365,221,152,93},[{2,60,38},{2,61,39}],0}, 151 | {{'M',9},{432,262,180,111},[{3,58,36},{2,59,37}],0}, 152 | {{'M',10},{513,311,213,131},[{4,69,43},{1,70,44}],0}, 153 | {{'M',11},{604,366,251,155},[{1,80,50},{4,81,51}],0}, 154 | {{'M',12},{691,419,287,177},[{6,58,36},{2,59,37}],0}, 155 | {{'M',13},{796,483,331,204},[{8,59,37},{1,60,38}],0}, 156 | {{'M',14},{871,528,362,223},[{4,64,40},{5,65,41}],3}, 157 | {{'M',15},{991,600,412,254},[{5,65,41},{5,66,42}],3}, 158 | {{'M',16},{1082,656,450,277},[{7,73,45},{3,74,46}],3}, 159 | {{'M',17},{1212,734,504,310},[{10,74,46},{1,75,47}],3}, 160 | {{'M',18},{1346,816,560,345},[{9,69,43},{4,70,44}],3}, 161 | {{'M',19},{1500,909,624,384},[{3,70,44},{11,71,45}],3}, 162 | {{'M',20},{1600,970,666,410},[{3,67,41},{13,68,42}],3}, 163 | {{'M',21},{1708,1035,711,438},[{17,68,42}],4}, 164 | {{'M',22},{1872,1134,779,480},[{17,74,46}],4}, 165 | {{'M',23},{2059,1248,857,528},[{4,75,47},{14,76,48}],4}, 166 | {{'M',24},{2188,1326,911,561},[{6,73,45},{14,74,46}],4}, 167 | {{'M',25},{2395,1451,997,614},[{8,75,47},{13,76,48}],4}, 168 | {{'M',26},{2544,1542,1059,652},[{19,74,46},{4,75,47}],4}, 169 | {{'M',27},{2701,1637,1125,692},[{22,73,45},{3,74,46}],4}, 170 | {{'M',28},{2857,1732,1190,732},[{3,73,45},{23,74,46}],3}, 171 | {{'M',29},{3035,1839,1264,778},[{21,73,45},{7,74,46}],3}, 172 | {{'M',30},{3289,1994,1370,843},[{19,75,47},{10,76,48}],3}, 173 | {{'M',31},{3486,2113,1452,894},[{2,74,46},{29,75,47}],3}, 174 | {{'M',32},{3693,2238,1538,947},[{10,74,46},{23,75,47}],3}, 175 | {{'M',33},{3909,2369,1628,1002},[{14,74,46},{21,75,47}],3}, 176 | {{'M',34},{4134,2506,1722,1060},[{14,74,46},{23,75,47}],3}, 177 | {{'M',35},{4343,2632,1809,1113},[{12,75,47},{26,76,48}],0}, 178 | {{'M',36},{4588,2780,1911,1176},[{6,75,47},{34,76,48}],0}, 179 | {{'M',37},{4775,2894,1989,1224},[{29,74,46},{14,75,47}],0}, 180 | {{'M',38},{5039,3054,2099,1292},[{13,74,46},{32,75,47}],0}, 181 | {{'M',39},{5313,3220,2213,1362},[{40,75,47},{7,76,48}],0}, 182 | {{'M',40},{5596,3391,2331,1435},[{18,75,47},{31,76,48}],0}, 183 | {{'Q',1},{27,16,11,7},[{1,26,13}],0}, 184 | {{'Q',2},{48,29,20,12},[{1,44,22}],7}, 185 | {{'Q',3},{77,47,32,20},[{2,35,17}],7}, 186 | {{'Q',4},{111,67,46,28},[{2,50,24}],7}, 187 | {{'Q',5},{144,87,60,37},[{2,33,15},{2,34,16}],7}, 188 | {{'Q',6},{178,108,74,45},[{4,43,19}],7}, 189 | {{'Q',7},{207,125,86,53},[{2,32,14},{4,33,15}],0}, 190 | {{'Q',8},{259,157,108,66},[{4,40,18},{2,41,19}],0}, 191 | {{'Q',9},{312,189,130,80},[{4,36,16},{4,37,17}],0}, 192 | {{'Q',10},{364,221,151,93},[{6,43,19},{2,44,20}],0}, 193 | {{'Q',11},{427,259,177,109},[{4,50,22},{4,51,23}],0}, 194 | {{'Q',12},{489,296,203,125},[{4,46,20},{6,47,21}],0}, 195 | {{'Q',13},{580,352,241,149},[{8,44,20},{4,45,21}],0}, 196 | {{'Q',14},{621,376,258,159},[{11,36,16},{5,37,17}],3}, 197 | {{'Q',15},{703,426,292,180},[{5,54,24},{7,55,25}],3}, 198 | {{'Q',16},{775,470,322,198},[{15,43,19},{2,44,20}],3}, 199 | {{'Q',17},{876,531,364,224},[{1,50,22},{15,51,23}],3}, 200 | {{'Q',18},{948,574,394,243},[{17,50,22},{1,51,23}],3}, 201 | {{'Q',19},{1063,644,442,272},[{17,47,21},{4,48,22}],3}, 202 | {{'Q',20},{1159,702,482,297},[{15,54,24},{5,55,25}],3}, 203 | {{'Q',21},{1224,742,509,314},[{17,50,22},{6,51,23}],4}, 204 | {{'Q',22},{1358,823,565,348},[{7,54,24},{16,55,25}],4}, 205 | {{'Q',23},{1468,890,611,376},[{11,54,24},{14,55,25}],4}, 206 | {{'Q',24},{1588,963,661,407},[{11,54,24},{16,55,25}],4}, 207 | {{'Q',25},{1718,1041,715,440},[{7,54,24},{22,55,25}],4}, 208 | {{'Q',26},{1804,1094,751,462},[{28,50,22},{6,51,23}],4}, 209 | {{'Q',27},{1933,1172,805,496},[{8,53,23},{26,54,24}],4}, 210 | {{'Q',28},{2085,1263,868,534},[{4,54,24},{31,55,25}],3}, 211 | {{'Q',29},{2181,1322,908,559},[{1,53,23},{37,54,24}],3}, 212 | {{'Q',30},{2358,1429,982,604},[{15,54,24},{25,55,25}],3}, 213 | {{'Q',31},{2473,1499,1030,634},[{42,54,24},{1,55,25}],3}, 214 | {{'Q',32},{2670,1618,1112,684},[{10,54,24},{35,55,25}],3}, 215 | {{'Q',33},{2805,1700,1168,719},[{29,54,24},{19,55,25}],3}, 216 | {{'Q',34},{2949,1787,1228,756},[{44,54,24},{7,55,25}],3}, 217 | {{'Q',35},{3081,1867,1283,790},[{39,54,24},{14,55,25}],0}, 218 | {{'Q',36},{3244,1966,1351,832},[{46,54,24},{10,55,25}],0}, 219 | {{'Q',37},{3417,2071,1423,876},[{49,54,24},{10,55,25}],0}, 220 | {{'Q',38},{3599,2181,1499,923},[{48,54,24},{14,55,25}],0}, 221 | {{'Q',39},{3791,2298,1579,972},[{43,54,24},{22,55,25}],0}, 222 | {{'Q',40},{3993,2420,1663,1024},[{34,54,24},{34,55,25}],0}, 223 | {{'H',1},{17,10,7,4},[{1,26,9}],0}, 224 | {{'H',2},{34,20,14,8},[{1,44,16}],7}, 225 | {{'H',3},{58,35,24,15},[{2,35,13}],7}, 226 | {{'H',4},{82,50,34,21},[{4,25,9}],7}, 227 | {{'H',5},{106,64,44,27},[{2,33,11},{2,34,12}],7}, 228 | {{'H',6},{139,84,58,36},[{4,43,15}],7}, 229 | {{'H',7},{154,93,64,39},[{4,39,13},{1,40,14}],0}, 230 | {{'H',8},{202,122,84,52},[{4,40,14},{2,41,15}],0}, 231 | {{'H',9},{235,143,98,60},[{4,36,12},{4,37,13}],0}, 232 | {{'H',10},{288,174,119,74},[{6,43,15},{2,44,16}],0}, 233 | {{'H',11},{331,200,137,85},[{3,36,12},{8,37,13}],0}, 234 | {{'H',12},{374,227,155,96},[{7,42,14},{4,43,15}],0}, 235 | {{'H',13},{427,259,177,109},[{12,33,11},{4,34,12}],0}, 236 | {{'H',14},{468,283,194,120},[{11,36,12},{5,37,13}],3}, 237 | {{'H',15},{530,321,220,136},[{11,36,12},{7,37,13}],3}, 238 | {{'H',16},{602,365,250,154},[{3,45,15},{13,46,16}],3}, 239 | {{'H',17},{674,408,280,173},[{2,42,14},{17,43,15}],3}, 240 | {{'H',18},{746,452,310,191},[{2,42,14},{19,43,15}],3}, 241 | {{'H',19},{813,493,338,208},[{9,39,13},{16,40,14}],3}, 242 | {{'H',20},{919,557,382,235},[{15,43,15},{10,44,16}],3}, 243 | {{'H',21},{969,587,403,248},[{19,46,16},{6,47,17}],4}, 244 | {{'H',22},{1056,640,439,270},[{34,37,13}],4}, 245 | {{'H',23},{1108,672,461,284},[{16,45,15},{14,46,16}],4}, 246 | {{'H',24},{1228,744,511,315},[{30,46,16},{2,47,17}],4}, 247 | {{'H',25},{1286,779,535,330},[{22,45,15},{13,46,16}],4}, 248 | {{'H',26},{1425,864,593,365},[{33,46,16},{4,47,17}],4}, 249 | {{'H',27},{1501,910,625,385},[{12,45,15},{28,46,16}],4}, 250 | {{'H',28},{1581,958,658,405},[{11,45,15},{31,46,16}],3}, 251 | {{'H',29},{1677,1016,698,430},[{19,45,15},{26,46,16}],3}, 252 | {{'H',30},{1782,1080,742,457},[{23,45,15},{25,46,16}],3}, 253 | {{'H',31},{1897,1150,790,486},[{23,45,15},{28,46,16}],3}, 254 | {{'H',32},{2022,1226,842,518},[{19,45,15},{35,46,16}],3}, 255 | {{'H',33},{2157,1307,898,553},[{11,45,15},{46,46,16}],3}, 256 | {{'H',34},{2301,1394,958,590},[{59,46,16},{1,47,17}],3}, 257 | {{'H',35},{2361,1431,983,605},[{22,45,15},{41,46,16}],0}, 258 | {{'H',36},{2524,1530,1051,647},[{2,45,15},{64,46,16}],0}, 259 | {{'H',37},{2625,1591,1093,673},[{24,45,15},{46,46,16}],0}, 260 | {{'H',38},{2735,1658,1139,701},[{42,45,15},{32,46,16}],0}, 261 | {{'H',39},{2927,1774,1219,750},[{10,45,15},{67,46,16}],0}, 262 | {{'H',40},{3057,1852,1273,784},[{20,45,15},{61,46,16}],0} 263 | ]). 264 | 265 | --------------------------------------------------------------------------------