Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
62.01% covered (warning)
62.01%
591 / 953
26.03% covered (danger)
26.03%
19 / 73
CRAP
0.00% covered (danger)
0.00%
0 / 1
QR
62.01% covered (warning)
62.01%
591 / 953
26.03% covered (danger)
26.03%
19 / 73
7707.91
0.00% covered (danger)
0.00%
0 / 1
 generateCodeArray
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
6.29
 binarize
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 encodeString
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 encodeMask
87.50% covered (warning)
87.50%
35 / 40
0.00% covered (danger)
0.00%
0 / 1
9.16
 getFrameAt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNextPosition
90.91% covered (success)
90.91%
30 / 33
0.00% covered (danger)
0.00%
0 / 1
10.08
 init
52.50% covered (warning)
52.50%
21 / 40
0.00% covered (danger)
0.00%
0 / 1
7.68
 getCode
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
4.06
 writeFormatInformation
96.00% covered (success)
96.00%
24 / 25
0.00% covered (danger)
0.00%
0 / 1
11
 generateMaskNo
75.00% covered (warning)
75.00%
24 / 32
0.00% covered (danger)
0.00%
0 / 1
15.64
 makeMaskNo
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
5.01
 makeMask
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 calcN1N3
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
15
 evaluateSymbol
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
1 / 1
17
 mask
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
5
 isdigitat
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 isalnumat
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 identifyMode
46.67% covered (danger)
46.67%
7 / 15
0.00% covered (danger)
0.00%
0 / 1
25.17
 eatNum
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
42
 eatAn
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
56
 eatKanji
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 eat8
65.62% covered (warning)
65.62%
21 / 32
0.00% covered (danger)
0.00%
0 / 1
12.29
 splitString
44.44% covered (danger)
44.44%
8 / 18
0.00% covered (danger)
0.00%
0 / 1
22.89
 toUpper
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 newInputItem
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
3.05
 encodeModeNum
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 encodeModeAn
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 encodeMode8
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 encodeModeKanji
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 encodeModeStructure
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 encodeBitStream
31.82% covered (danger)
31.82%
7 / 22
0.00% covered (danger)
0.00%
0 / 1
22.53
 appendNewInputItem
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 insertStructuredAppendHeader
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 calcParity
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 checkModeNum
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 lookAnTable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 checkModeAn
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 estimateBitsModeNum
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 estimateBitsModeAn
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 estimateBitsMode8
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 estimateBitsModeKanji
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 checkModeKanji
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
56
 check
25.00% covered (danger)
25.00%
2 / 8
0.00% covered (danger)
0.00%
0 / 1
27.67
 estimateBitStreamSize
60.00% covered (warning)
60.00%
12 / 20
0.00% covered (danger)
0.00%
0 / 1
14.18
 estimateVersion
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
2.01
 lengthOfCode
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
156
 createBitStream
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 convertData
81.25% covered (warning)
81.25%
13 / 16
0.00% covered (danger)
0.00%
0 / 1
6.24
 appendPaddingBit
85.00% covered (warning)
85.00%
17 / 20
0.00% covered (danger)
0.00%
0 / 1
7.17
 mergeBitStream
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
 getBitStream
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getByteStream
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 allocate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFromNum
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 newFromBytes
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 appendBitstream
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 appendNum
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 appendBytes
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 bitstreamToByte
66.67% covered (warning)
66.67%
14 / 21
0.00% covered (danger)
0.00%
0 / 1
7.33
 qrstrset
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 getMinimumVersion
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 lengthIndicator
50.00% covered (danger)
50.00%
4 / 8
0.00% covered (danger)
0.00%
0 / 1
6.00
 maximumWords
58.33% covered (warning)
58.33%
7 / 12
0.00% covered (danger)
0.00%
0 / 1
6.81
 getEccSpec
66.67% covered (warning)
66.67%
12 / 18
0.00% covered (danger)
0.00%
0 / 1
3.33
 putAlignmentMarker
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 putAlignmentPattern
33.33% covered (danger)
33.33%
8 / 24
0.00% covered (danger)
0.00%
0 / 1
21.52
 putFinderPattern
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 createFrame
70.45% covered (warning)
70.45%
31 / 44
0.00% covered (danger)
0.00%
0 / 1
12.58
 newFrame
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
5.58
 init_rs
50.00% covered (danger)
50.00%
4 / 8
0.00% covered (danger)
0.00%
0 / 1
16.00
 modnn
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 init_rs_char
91.30% covered (success)
91.30%
42 / 46
0.00% covered (danger)
0.00%
0 / 1
19.24
 encode_rs_char
95.65% covered (success)
95.65%
22 / 23
0.00% covered (danger)
0.00%
0 / 1
5
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Utils\Barcode
8 * @author    Nicola Asuni - Tecnick.com LTD - www.tecnick.com <info@tecnick.com>
9 * @copyright Copyright (C) 2010 - 2014  Nicola Asuni - Tecnick.com LTD
10 * @license   GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
11 * @version   1.0.0
12 * @link      https://jingga.app
13 */
14declare(strict_types=1);
15
16namespace phpOMS\Utils\Barcode;
17
18/**
19 * QR class.
20 *
21 * @package phpOMS\Utils\Barcode
22 * @license GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
23 * @link    https://jingga.app
24 * @since   1.0.0
25 */
26class QR extends TwoDAbstract
27{
28    /**
29     * Error correction level.
30     *
31     * @var int
32     * @since 1.0.0
33     */
34    public const QR_ECLEVEL_L = 0;
35
36    /**
37     * Error correction level.
38     *
39     * @var int
40     * @since 1.0.0
41     */
42    public const QR_ECLEVEL_M = 1;
43
44    /**
45     * Error correction level.
46     *
47     * @var int
48     * @since 1.0.0
49     */
50    public const QR_ECLEVEL_Q = 2;
51
52    /**
53     * Error correction level.
54     *
55     * @var int
56     * @since 1.0.0
57     */
58    public const QR_ECLEVEL_H = 3;
59
60    private const QRCODEDEFS = true;
61
62    private const QR_MODE_NL = -1;
63
64    private const QR_MODE_NM = 0;
65
66    private const QR_MODE_AN = 1;
67
68    private const QR_MODE_8B = 2;
69
70    private const QR_MODE_KJ = 3;
71
72    private const QR_MODE_ST = 4;
73
74    private const QRSPEC_VERSION_MAX = 40;
75
76    private const QRSPEC_WIDTH_MAX = 177;
77
78    private const QRCAP_WIDTH = 0;
79
80    private const QRCAP_WORDS = 1;
81
82    private const QRCAP_REMINDER = 2;
83
84    private const QRCAP_EC = 3;
85
86    private const STRUCTURE_HEADER_BITS = 20;
87
88    private const MAX_STRUCTURED_SYMBOLS = 16;
89
90    private const N1 = 3;
91
92    private const N2 = 3;
93
94    private const N3 = 40;
95
96    private const N4 = 10;
97
98    private const QR_FIND_BEST_MASK = true;
99
100    private const QR_FIND_FROM_RANDOM = 2;
101
102    private const QR_DEFAULT_MASK = 2;
103
104    /**
105     * Alphabet-numeric convesion table.
106     *
107     * @var array
108     * @since 1.0.0
109     */
110    private const AN_TABLE = [
111        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
112        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
113        36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
114         0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 44, -1, -1, -1, -1, -1,
115        -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
116        25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
117        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
118        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
119    ];
120
121    /**
122     * Array Table of the capacity of symbols.
123     * See Table 1 (pp.13) and Table 12-16 (pp.30-36), JIS X0510:2004.
124     *
125     * @var array
126     * @since 1.0.0
127     */
128    private const CAPACITY = [
129        [0,    0, 0, [0,    0,    0,    0]],
130        [21,   26, 0, [7,   10,   13,   17]], //  1
131        [25,   44, 7, [10,   16,   22,   28]],
132        [29,   70, 7, [15,   26,   36,   44]],
133        [33,  100, 7, [20,   36,   52,   64]],
134        [37,  134, 7, [26,   48,   72,   88]], //  5
135        [41,  172, 7, [36,   64,   96,  112]],
136        [45,  196, 0, [40,   72,  108,  130]],
137        [49,  242, 0, [48,   88,  132,  156]],
138        [53,  292, 0, [60,  110,  160,  192]],
139        [57,  346, 0, [72,  130,  192,  224]], // 10
140        [61,  404, 0, [80,  150,  224,  264]],
141        [65,  466, 0, [96,  176,  260,  308]],
142        [69,  532, 0, [104,  198,  288,  352]],
143        [73,  581, 3, [120,  216,  320,  384]],
144        [77,  655, 3, [132,  240,  360,  432]], // 15
145        [81,  733, 3, [144,  280,  408,  480]],
146        [85,  815, 3, [168,  308,  448,  532]],
147        [89,  901, 3, [180,  338,  504,  588]],
148        [93,  991, 3, [196,  364,  546,  650]],
149        [97, 1085, 3, [224,  416,  600,  700]], // 20
150        [101, 1156, 4, [224,  442,  644,  750]],
151        [105, 1258, 4, [252,  476,  690,  816]],
152        [109, 1364, 4, [270,  504,  750,  900]],
153        [113, 1474, 4, [300,  560,  810,  960]],
154        [117, 1588, 4, [312,  588,  870, 1050]], // 25
155        [121, 1706, 4, [336,  644,  952, 1110]],
156        [125, 1828, 4, [360,  700, 1020, 1200]],
157        [129, 1921, 3, [390,  728, 1050, 1260]],
158        [133, 2051, 3, [420,  784, 1140, 1350]],
159        [137, 2185, 3, [450,  812, 1200, 1440]], // 30
160        [141, 2323, 3, [480,  868, 1290, 1530]],
161        [145, 2465, 3, [510,  924, 1350, 1620]],
162        [149, 2611, 3, [540,  980, 1440, 1710]],
163        [153, 2761, 3, [570, 1036, 1530, 1800]],
164        [157, 2876, 0, [570, 1064, 1590, 1890]], // 35
165        [161, 3034, 0, [600, 1120, 1680, 1980]],
166        [165, 3196, 0, [630, 1204, 1770, 2100]],
167        [169, 3362, 0, [660, 1260, 1860, 2220]],
168        [173, 3532, 0, [720, 1316, 1950, 2310]],
169        [177, 3706, 0, [750, 1372, 2040, 2430]],  // 40
170    ];
171
172    /**
173     * Array Length indicator.
174     *
175     * @var array
176     * @since 1.0.0
177     */
178    private const LENGTH_TABLE_BITS = [
179        [10, 12, 14],
180        [9, 11, 13],
181        [8, 16, 16],
182        [8, 10, 12],
183    ];
184
185    /**
186     * Array Table of the error correction code (Reed-Solomon block).
187     * See Table 12-16 (pp.30-36), JIS X0510:2004.
188     *
189     * @var array
190     * @since 1.0.0
191     */
192    private const ECC_TABLE = [
193        [[0,  0], [0,  0], [0,  0], [0,  0]],
194        [[1,  0], [1,  0], [1,  0], [1,  0]], //  1
195        [[1,  0], [1,  0], [1,  0], [1,  0]],
196        [[1,  0], [1,  0], [2,  0], [2,  0]],
197        [[1,  0], [2,  0], [2,  0], [4,  0]],
198        [[1,  0], [2,  0], [2,  2], [2,  2]], //  5
199        [[2,  0], [4,  0], [4,  0], [4,  0]],
200        [[2,  0], [4,  0], [2,  4], [4,  1]],
201        [[2,  0], [2,  2], [4,  2], [4,  2]],
202        [[2,  0], [3,  2], [4,  4], [4,  4]],
203        [[2,  2], [4,  1], [6,  2], [6,  2]], // 10
204        [[4,  0], [1,  4], [4,  4], [3,  8]],
205        [[2,  2], [6,  2], [4,  6], [7,  4]],
206        [[4,  0], [8,  1], [8,  4], [12,  4]],
207        [[3,  1], [4,  5], [11,  5], [11,  5]],
208        [[5,  1], [5,  5], [5,  7], [11,  7]], // 15
209        [[5,  1], [7,  3], [15,  2], [3, 13]],
210        [[1,  5], [10,  1], [1, 15], [2, 17]],
211        [[5,  1], [9,  4], [17,  1], [2, 19]],
212        [[3,  4], [3, 11], [17,  4], [9, 16]],
213        [[3,  5], [3, 13], [15,  5], [15, 10]], // 20
214        [[4,  4], [17,  0], [17,  6], [19,  6]],
215        [[2,  7], [17,  0], [7, 16], [34,  0]],
216        [[4,  5], [4, 14], [11, 14], [16, 14]],
217        [[6,  4], [6, 14], [11, 16], [30,  2]],
218        [[8,  4], [8, 13], [7, 22], [22, 13]], // 25
219        [[10,  2], [19,  4], [28,  6], [33,  4]],
220        [[8,  4], [22,  3], [8, 26], [12, 28]],
221        [[3, 10], [3, 23], [4, 31], [11, 31]],
222        [[7,  7], [21,  7], [1, 37], [19, 26]],
223        [[5, 10], [19, 10], [15, 25], [23, 25]], // 30
224        [[13,  3], [2, 29], [42,  1], [23, 28]],
225        [[17,  0], [10, 23], [10, 35], [19, 35]],
226        [[17,  1], [14, 21], [29, 19], [11, 46]],
227        [[13,  6], [14, 23], [44,  7], [59,  1]],
228        [[12,  7], [12, 26], [39, 14], [22, 41]], // 35
229        [[6, 14], [6, 34], [46, 10], [2, 64]],
230        [[17,  4], [29, 14], [49, 10], [24, 46]],
231        [[4, 18], [13, 32], [48, 14], [42, 32]],
232        [[20,  4], [40,  7], [43, 22], [10, 67]],
233        [[19,  6], [18, 31], [34, 34], [20, 61]], // 40
234    ];
235
236    /**
237     * Array Positions of alignment patterns.
238     * This array includes only the second and the third position of the alignment patterns. Rest of them can be calculated from the distance between them.
239     * See Table 1 in Appendix E (pp.71) of JIS X0510:2004.
240     *
241     * @var array
242     * @since 1.0.0
243     */
244    private const ALIGNMENT_PATTERN = [
245        [0,  0],
246        [0,  0], [18,  0], [22,  0], [26,  0], [30,  0], //  1- 5
247        [34,  0], [22, 38], [24, 42], [26, 46], [28, 50], //  6-10
248        [30, 54], [32, 58], [34, 62], [26, 46], [26, 48], // 11-15
249        [26, 50], [30, 54], [30, 56], [30, 58], [34, 62], // 16-20
250        [28, 50], [26, 50], [30, 54], [28, 54], [32, 58], // 21-25
251        [30, 58], [34, 62], [26, 50], [30, 54], [26, 52], // 26-30
252        [30, 56], [34, 60], [30, 58], [34, 62], [30, 54], // 31-35
253        [24, 50], [28, 54], [32, 58], [26, 54], [30, 58], // 35-40
254    ];
255
256    /**
257     * Array Version information pattern (BCH coded).
258     * See Table 1 in Appendix D (pp.68) of JIS X0510:2004.
259     * size: [QRSPEC_VERSION_MAX - 6]
260     *
261     * @var array
262     * @since 1.0.0
263     */
264    private const VERSION_PATTERN = [
265        0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
266        0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
267        0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
268        0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
269        0x27541, 0x28c69,
270    ];
271
272    /**
273     * Array Format information
274     *
275     * @var array
276     * @since 1.0.0
277     */
278    private const FORMAT_INFO = [
279        [0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976],
280        [0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0],
281        [0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed],
282        [0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b],
283    ];
284
285    private const PUT_ALIGNMENT_FILTER = [
286        "\xa1\xa1\xa1\xa1\xa1",
287        "\xa1\xa0\xa0\xa0\xa1",
288        "\xa1\xa0\xa1\xa0\xa1",
289        "\xa1\xa0\xa0\xa0\xa1",
290        "\xa1\xa1\xa1\xa1\xa1",
291    ];
292
293    private const PUT_FINDER = [
294        "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
295        "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
296        "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
297        "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
298        "\xc1\xc0\xc1\xc1\xc1\xc0\xc1",
299        "\xc1\xc0\xc0\xc0\xc0\xc0\xc1",
300        "\xc1\xc1\xc1\xc1\xc1\xc1\xc1",
301    ];
302
303    /**
304     * Version.
305     *
306     * @var int
307     * @since 1.0.0
308     */
309    protected int $version = 0;
310
311    /**
312     * Levels of error correction. See definitions for possible values.
313     *
314     * @var int
315     * @since 1.0.0
316     */
317    public int $level = self::QR_ECLEVEL_L;
318
319    /**
320     * Encoding mode.
321     *
322     * @var int
323     * @since 1.0.0
324     */
325    protected int $hint = self::QR_MODE_8B;
326
327    /**
328     * Boolean flag, if true the input string will be converted to uppercase.
329     *
330     * @var bool
331     * @since 1.0.0
332     */
333    protected bool $casesensitive = true;
334
335    /**
336     * Structured QR code (not supported yet).
337     *
338     * @var int
339     * @since 1.0.0
340     */
341    protected int $structured = 0;
342
343    /**
344     * Mask data.
345     *
346     * @var array
347     * @since 1.0.0
348     */
349    protected array $data = [];
350
351    /**
352     * Width.
353     *
354     * @var int
355     * @since 1.0.0
356     */
357    protected int $width = 0;
358
359    /**
360     * Frame.
361     *
362     * @var array
363     * @since 1.0.0
364     */
365    protected array $frame = [];
366
367    /**
368     * X position of bit.
369     *
370     * @var int
371     * @since 1.0.0
372     */
373    protected int $x = 0;
374
375    /**
376     * Y position of bit.
377     *
378     * @var int
379     * @since 1.0.0
380     */
381    protected int $y = 0;
382
383    /**
384     * Direction.
385     *
386     * @var int
387     * @since 1.0.0
388     */
389    protected int $dir = 0;
390
391    /**
392     * Single bit value.
393     *
394     * @var int
395     * @since 1.0.0
396     */
397    protected int $bit = 0;
398
399    /**
400     * Data code.
401     *
402     * @var array
403     * @since 1.0.0
404     */
405    protected array $datacode = [];
406
407    /**
408     * Error correction code.
409     *
410     * @var array
411     * @since 1.0.0
412     */
413    protected array $ecccode = [];
414
415    /**
416     * Blocks.
417     *
418     * @var int
419     *
420     * @since 1.0.0
421     */
422    protected int $blocks = 0;
423
424    /**
425     * Reed-Solomon blocks.
426     *
427     * @var array
428     * @since 1.0.0
429     */
430    protected array $rsblocks = []; //of RSblock
431
432    /**
433     * Counter.
434     *
435     * @var int
436     * @since 1.0.0
437     */
438    protected int $count = 0;
439
440    /**
441     * Data length.
442     *
443     * @var int
444     * @since 1.0.0
445     */
446    protected int $dataLength = 0;
447
448    /**
449     * Error correction length.
450     *
451     * @var int
452     * @since 1.0.0
453     */
454    protected int $eccLength = 0;
455
456    /**
457     * Value b1.
458     *
459     * @var int
460     * @since 1.0.0
461     */
462    protected int $b1 = 0;
463
464    /**
465     * Run length.
466     *
467     * @var array
468     * @since 1.0.0
469     */
470    protected array $runLength = [];
471
472    /**
473     * Input data string.
474     *
475     * @var string
476     * @since 1.0.0
477     */
478    protected string $dataStr = '';
479
480    /**
481     * Input items.
482     *
483     * @var array
484     * @since 1.0.0
485     */
486    protected array $items = [];
487
488    /**
489     * Reed-Solomon items.
490     *
491     * @var array
492     * @since 1.0.0
493     */
494    protected array $rsitems = [];
495
496    /**
497     * Array of frames.
498     *
499     * @var array
500     * @since 1.0.0
501     */
502    protected array $frames = [];
503
504    /**
505     * {@inheritdoc}
506     */
507    public function generateCodeArray() : array
508    {
509        $this->codearray = [];
510
511        if (($this->hint !== self::QR_MODE_8B && $this->hint !== self::QR_MODE_KJ)
512            || ($this->version < 0 || $this->version > self::QRSPEC_VERSION_MAX)
513        ) {
514            return [];
515        }
516
517        $this->items = [];
518        $this->encodeString($this->content);
519
520        if (empty($this->data)) {
521            return [];
522        }
523
524        $this->codearray = $this->binarize($this->data);
525
526        return $this->codearray;
527    }
528
529    /**
530     * Convert the frame in binary form
531     *
532     * @return array
533     *
534     * @since 1.0.0
535     */
536    protected function binarize(array $frame) : array
537    {
538        $bin = [[]];
539
540        foreach ($frame as $row => $cols) {
541            $len = \strlen($cols);
542            for ($i = 0; $i < $len; ++$i) {
543                $bin[$row][$i] = \ord($frame[$row][$i]) & 1;
544            }
545        }
546
547        return $bin;
548    }
549
550    /**
551     * Encode the input string to QR code
552     *
553     * @return void
554     *
555     * @since 1.0.0
556     */
557    protected function encodeString(string $string) : void
558    {
559        $this->dataStr = $string;
560        if (!$this->casesensitive) {
561            $this->toUpper();
562        }
563
564        $ret = $this->splitString();
565        if ($ret < 0) {
566            return;
567        }
568
569        $this->encodeMask(-1);
570    }
571
572    /**
573     * Encode mask
574     *
575     * @return void
576     *
577     * @since 1.0.0
578     */
579    protected function encodeMask(int $mask) : void
580    {
581        $spec           = [0, 0, 0, 0, 0];
582        $this->datacode = $this->getByteStream($this->items);
583
584        if (empty($this->datacode)) {
585            return;
586        }
587
588        $spec             = $this->getEccSpec($this->version, $this->level, $spec);
589        $this->b1         = $spec[0];
590        $this->dataLength = $spec[0] * $spec[1] + $spec[3] * $spec[4];
591        $this->eccLength  = ($spec[0] + $spec[3]) * $spec[2];
592        $this->ecccode    = \array_fill(0, $this->eccLength, 0);
593        $this->blocks     = $spec[0] + $spec[3];
594        $ret              = $this->init($spec);
595
596        if ($ret < 0) {
597            return;
598        }
599
600        $this->count = 0;
601        $this->width = self::CAPACITY[$this->version][self::QRCAP_WIDTH];
602        $this->frame = $this->newFrame($this->version);
603        $this->x     = $this->width - 1;
604        $this->y     = $this->width - 1;
605        $this->dir   = -1;
606        $this->bit   = -1;
607
608        // inteleaved data and ecc codes
609        for ($i = 0; $i < $this->dataLength + $this->eccLength; ++$i) {
610            $code = $this->getCode();
611            $bit  = 0x80;
612
613            for ($j = 0; $j < 8; ++$j) {
614                $addr                                = $this->getNextPosition();
615                $this->frame[$addr['y']][$addr['x']] = 0x02 | (($bit & $code) !== 0);
616                $bit                               >>= 1;
617            }
618        }
619
620        // remainder bits
621        $j = self::CAPACITY[$this->version][self::QRCAP_REMINDER];
622        for ($i = 0; $i < $j; ++$i) {
623            $addr                                = $this->getNextPosition();
624            $this->frame[$addr['y']][$addr['x']] = 0x02;
625        }
626
627        // masking
628        $this->runLength = \array_fill(0, self::QRSPEC_WIDTH_MAX + 1, 0);
629        if ($mask < 0) {
630            $masked = self::QR_FIND_BEST_MASK
631                ? $this->mask($this->width, $this->frame, $this->level)
632                : $this->makeMask($this->width, $this->frame, (self::QR_DEFAULT_MASK % 8), $this->level);
633        } else {
634            $masked = $this->makeMask($this->width, $this->frame, $mask, $this->level);
635        }
636
637        if (empty($masked)) {
638            return;
639        }
640
641        $this->data = $masked;
642    }
643
644    /**
645     * Get frame value at specified position
646     *
647     * @return int
648     *
649     * @since 1.0.0
650     */
651    protected function getFrameAt(array $at) : int
652    {
653        return \ord($this->frame[$at['y']][$at['x']]);
654    }
655
656    /**
657     * Return the next frame position
658     *
659     * @return array
660     *
661     * @since 1.0.0
662     */
663    protected function getNextPosition() : array
664    {
665        do {
666            if ($this->bit === -1) {
667                $this->bit = 0;
668
669                return ['x' => $this->x, 'y' => $this->y];
670            }
671
672            $x = $this->x;
673            $y = $this->y;
674            $w = $this->width;
675
676            if ($this->bit === 0) {
677                --$x;
678                ++$this->bit;
679            } else {
680                ++$x;
681                --$this->bit;
682
683                $y += $this->dir;
684            }
685
686            if ($this->dir < 0) {
687                if ($y < 0) {
688                    $y         = 0;
689                    $x        -= 2;
690                    $this->dir = 1;
691
692                    if ($x === 6) {
693                        --$x;
694                        $y = 9;
695                    }
696                }
697            } elseif ($y === $w) {
698                $y         = $w - 1;
699                $x        -= 2;
700                $this->dir = -1;
701
702                if ($x === 6) {
703                    --$x;
704                    $y -= 8;
705                }
706            }
707
708            if (($x < 0) || ($y < 0)) {
709                return [];
710            }
711
712            $this->x = $x;
713            $this->y = $y;
714        } while (\ord($this->frame[$y][$x]) & 0x80);
715
716        return ['x' => $x, 'y' => $y];
717    }
718
719    // - - - - - - - - - - - - - - - - - - - - - - - - -
720
721    // QRrawcode
722
723    /**
724     * Initialize code.
725     *
726     * @return int
727     *
728     * @since 1.0.0
729     */
730    protected function init(array $spec) : int
731    {
732        $dl      = $spec[1];
733        $el      = $spec[2];
734        $rs      = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
735        $blockNo = 0;
736        $dataPos = 0;
737        $eccPos  = 0;
738        $endfor  = $spec[0];
739
740        for ($i = 0; $i < $endfor; ++$i) {
741            $ecc                                    = \array_slice($this->ecccode, $eccPos);
742            $this->rsblocks[$blockNo]               = [];
743            $this->rsblocks[$blockNo]['dataLength'] = $dl;
744            $this->rsblocks[$blockNo]['data']       = \array_slice($this->datacode, $dataPos);
745            $this->rsblocks[$blockNo]['eccLength']  = $el;
746            $ecc                                    = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
747            $this->rsblocks[$blockNo]['ecc']        = $ecc;
748            $this->ecccode                          = \array_merge(\array_slice($this->ecccode, 0, $eccPos), $ecc);
749            $dataPos                               += $dl;
750            $eccPos                                += $el;
751
752            ++$blockNo;
753        }
754        if ($spec[3] === 0) {
755            return 0;
756        }
757
758        $dl = $spec[4];
759        $el = $spec[2];
760        $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el);
761
762        if ($rs === null) {
763            return -1;
764        }
765
766        $endfor = $spec[3];
767        for ($i = 0; $i < $endfor; ++$i) {
768            $ecc                                    = \array_slice($this->ecccode, $eccPos);
769            $this->rsblocks[$blockNo]               = [];
770            $this->rsblocks[$blockNo]['dataLength'] = $dl;
771            $this->rsblocks[$blockNo]['data']       = \array_slice($this->datacode, $dataPos);
772            $this->rsblocks[$blockNo]['eccLength']  = $el;
773            $ecc                                    = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc);
774            $this->rsblocks[$blockNo]['ecc']        = $ecc;
775            $this->ecccode                          = \array_merge(\array_slice($this->ecccode, 0, $eccPos), $ecc);
776            $dataPos                               += $dl;
777            $eccPos                                += $el;
778
779            ++$blockNo;
780        }
781
782        return 0;
783    }
784
785    /**
786     * Return Reed-Solomon block
787     *
788     * @return int
789     *
790     * @since 1.0.0code.
791     */
792    protected function getCode() : int
793    {
794        if ($this->count < $this->dataLength) {
795            $row = $this->count % $this->blocks;
796            $col = $this->count / $this->blocks;
797
798            if ($col >= $this->rsblocks[0]['dataLength']) {
799                $row += $this->b1;
800            }
801
802            $ret = $this->rsblocks[$row]['data'][$col];
803        } elseif ($this->count < $this->dataLength + $this->eccLength) {
804            $row = ($this->count - $this->dataLength) % $this->blocks;
805            $col = ($this->count - $this->dataLength) / $this->blocks;
806            $ret = $this->rsblocks[$row]['ecc'][$col];
807        } else {
808            return 0;
809        }
810
811        ++$this->count;
812
813        return $ret;
814    }
815
816    /**
817     * Write Format Information on frame and returns the number of black bits
818     *
819     * @return int
820     *
821     * @since 1.0.0
822     */
823    protected function writeFormatInformation(int $width, array &$frame, int $mask, int $level) : int
824    {
825        $blacks = 0;
826        $format = $mask < 0 || $mask > 7 || $level < 0 || $level > 3
827            ? 0
828            : self::FORMAT_INFO[$level][$mask];
829
830        for ($i = 0; $i < 8; ++$i) {
831            if (($format & 1) !== 0) {
832                $blacks += 2;
833                $v       = 0x85;
834            } else {
835                $v = 0x84;
836            }
837
838            $frame[8][$width - 1 - $i] = \chr($v);
839            if ($i < 6) {
840                $frame[$i][8] = \chr($v);
841            } else {
842                $frame[$i + 1][8] = \chr($v);
843            }
844
845            $format >>= 1;
846        }
847
848        for ($i = 0; $i < 7; ++$i) {
849            if (($format & 1) !== 0) {
850                $blacks += 2;
851                $v       = 0x85;
852            } else {
853                $v = 0x84;
854            }
855
856            $frame[$width - 7 + $i][8] = \chr($v);
857            if ($i === 0) {
858                $frame[8][7] = \chr($v);
859            } else {
860                $frame[8][6 - $i] = \chr($v);
861            }
862
863            $format >>= 1;
864        }
865
866        return $blacks;
867    }
868
869    /**
870     * Return bitmask
871     *
872     * @return array
873     *
874     * @since 1.0.0
875     */
876    protected function generateMaskNo(int $maskNo, int $width, array $frame) : array
877    {
878        $bitMask = \array_fill(0, $width, \array_fill(0, $width, 0));
879        for ($y = 0; $y < $width; ++$y) {
880            for ($x = 0; $x < $width; ++$x) {
881                if ((\ord($frame[$y][$x]) & 0x80) !== 0) {
882                    $bitMask[$y][$x] = 0;
883                } else {
884                    $maskFunc = 0;
885                    switch ($maskNo) {
886                        case 0:
887                            $maskFunc = ($x + $y) & 1;
888                            break;
889                        case 1:
890                            $maskFunc = $y & 1;
891                            break;
892                        case 2:
893                            $maskFunc = $x % 3;
894                            break;
895                        case 3:
896                            $maskFunc = ($x + $y) % 3;
897                            break;
898                        case 4:
899                            $maskFunc = (((int) ($y / 2)) + ((int) ($x / 3))) & 1;
900                            break;
901                        case 5:
902                            $maskFunc = (($x * $y) & 1) + ($x * $y) % 3;
903                            break;
904                        case 6:
905                            $maskFunc = ((($x * $y) & 1) + ($x * $y) % 3) & 1;
906                            break;
907                        case 7:
908                            $maskFunc = ((($x * $y) % 3) + (($x + $y) & 1)) & 1;
909                            break;
910                    }
911
912                    $bitMask[$y][$x] = $maskFunc === 0 ? 1 : 0;
913                }
914            }
915        }
916
917        return $bitMask;
918    }
919
920    /**
921     * makeMaskNo
922     *
923     * @return int
924     *
925     * @since 1.0.0
926     */
927    protected function makeMaskNo(int $maskNo, int $width, array $s, array &$d, bool $maskGenOnly = false) : int
928    {
929        $b       = 0;
930        $bitMask = [];
931        $bitMask = $this->generateMaskNo($maskNo, $width, $s);
932
933        if ($maskGenOnly) {
934            return 0;
935        }
936
937        $d = $s;
938        for ($y = 0; $y < $width; ++$y) {
939            for ($x = 0; $x < $width; ++$x) {
940                if ($bitMask[$y][$x] === 1) {
941                    $d[$y][$x] = \chr(\ord($s[$y][$x]) ^ ((int) ($bitMask[$y][$x])));
942                }
943
944                $b += (int) (\ord($d[$y][$x]) & 1);
945            }
946        }
947
948        return $b;
949    }
950
951    /**
952     * makeMask
953     *
954     * @return array
955     *
956     * @since 1.0.0
957     */
958    protected function makeMask(int $width, array $frame, int $maskNo, int $level) : array
959    {
960        $masked = \array_fill(0, $width, \str_repeat("\0", $width));
961        $this->makeMaskNo($maskNo, $width, $frame, $masked);
962        $this->writeFormatInformation($width, $masked, $maskNo, $level);
963
964        return $masked;
965    }
966
967    /**
968     * calcN1N3
969     *
970     * @return int
971     *
972     * @since 1.0.0
973     */
974    protected function calcN1N3(int $length) : int
975    {
976        $demerit = 0;
977        for ($i = 0; $i < $length; ++$i) {
978            if ($this->runLength[$i] >= 5) {
979                $demerit += (self::N1 + ($this->runLength[$i] - 5));
980            }
981
982            if (($i & 1) !== 0 && (($i >= 3)
983                && ($i < ($length - 2)) && ($this->runLength[$i] % 3 === 0))
984            ) {
985                $fact = (int) ($this->runLength[$i] / 3);
986
987                if (($this->runLength[$i - 2] === $fact)
988                    && ($this->runLength[$i - 1] === $fact)
989                    && ($this->runLength[$i + 1] === $fact)
990                    && ($this->runLength[$i + 2] === $fact)
991                ) {
992                    if (($this->runLength[$i - 3] < 0) || ($this->runLength[$i - 3] >= (4 * $fact))) {
993                        $demerit += self::N3;
994                    } elseif (($i + 3) >= $length || $this->runLength[$i + 3] >= (4 * $fact)) {
995                        $demerit += self::N3;
996                    }
997                }
998            }
999        }
1000
1001        return $demerit;
1002    }
1003
1004    /**
1005     * evaluateSymbol
1006     *
1007     * @return int
1008     *
1009     * @since 1.0.0
1010     */
1011    protected function evaluateSymbol(int $width, array $frame) : int
1012    {
1013        $head    = 0;
1014        $demerit = 0;
1015
1016        for ($y = 0; $y < $width; ++$y) {
1017            $head               = 0;
1018            $this->runLength[0] = 1;
1019            $frameY             = $frame[$y];
1020
1021            if ($y > 0) {
1022                $frameYM = $frame[$y - 1];
1023            }
1024
1025            for ($x = 0; $x < $width; ++$x) {
1026                if (($x > 0) && ($y > 0)) {
1027                    $b22 = \ord($frameY[$x]) & \ord($frameY[$x - 1]) & \ord($frameYM[$x]) & \ord($frameYM[$x - 1]);
1028                    $w22 = \ord($frameY[$x]) | \ord($frameY[$x - 1]) | \ord($frameYM[$x]) | \ord($frameYM[$x - 1]);
1029
1030                    if ((($b22 | ($w22 ^ 1)) & 1) !== 0) {
1031                        $demerit += self::N2;
1032                    }
1033                }
1034
1035                if (($x === 0) && (\ord($frameY[$x]) & 1)) {
1036                    $this->runLength[0]     = -1;
1037                    $head                   = 1;
1038                    $this->runLength[$head] = 1;
1039                } elseif ($x > 0) {
1040                    if (((\ord($frameY[$x]) ^ \ord($frameY[$x - 1])) & 1) !== 0) {
1041                        ++$head;
1042                        $this->runLength[$head] = 1;
1043                    } else {
1044                        ++$this->runLength[$head];
1045                    }
1046                }
1047            }
1048
1049            $demerit += $this->calcN1N3($head + 1);
1050        }
1051
1052        for ($x = 0; $x < $width; ++$x) {
1053            $head               = 0;
1054            $this->runLength[0] = 1;
1055
1056            for ($y = 0; $y < $width; ++$y) {
1057                if (($y === 0) && (\ord($frame[$y][$x]) & 1)) {
1058                    $this->runLength[0]     = -1;
1059                    $head                   = 1;
1060                    $this->runLength[$head] = 1;
1061                } elseif ($y > 0) {
1062                    if (((\ord($frame[$y][$x]) ^ \ord($frame[$y - 1][$x])) & 1) !== 0) {
1063                        ++$head;
1064                        $this->runLength[$head] = 1;
1065                    } else {
1066                        ++$this->runLength[$head];
1067                    }
1068                }
1069            }
1070
1071            $demerit += $this->calcN1N3($head + 1);
1072        }
1073
1074        return $demerit;
1075    }
1076
1077    /**
1078     * mask
1079     *
1080     * @return array
1081     *
1082     * @since 1.0.0
1083     */
1084    protected function mask(int $width, array $frame, int $level) : array
1085    {
1086        $minDemerit    = \PHP_INT_MAX;
1087        $bestMask      = [];
1088        $checked_masks = [0, 1, 2, 3, 4, 5, 6, 7];
1089
1090        if (self::QR_FIND_FROM_RANDOM !== false) {
1091            $howManuOut = 8 - (self::QR_FIND_FROM_RANDOM % 9);
1092            for ($i = 0; $i < $howManuOut; ++$i) {
1093                // @note: This is why the same content can result in different QR codes
1094                $remPos = \mt_rand(0, \count($checked_masks) - 1);
1095                unset($checked_masks[$remPos]);
1096                $checked_masks = \array_values($checked_masks);
1097            }
1098        }
1099
1100        $bestMask = $frame;
1101        foreach ($checked_masks as $i) {
1102            $mask     = \array_fill(0, $width, \str_repeat("\0", $width));
1103            $demerit  = 0;
1104            $blacks   = 0;
1105            $blacks   = $this->makeMaskNo($i, $width, $frame, $mask);
1106            $blacks  += $this->writeFormatInformation($width, $mask, $i, $level);
1107            $blacks   = (int) (100 * $blacks / ($width * $width));
1108            $demerit  = (int) ((int) (\abs($blacks - 50) / 5) * self::N4);
1109            $demerit += $this->evaluateSymbol($width, $mask);
1110
1111            if ($demerit < $minDemerit) {
1112                $minDemerit = $demerit;
1113                $bestMask   = $mask;
1114            }
1115        }
1116
1117        return $bestMask;
1118    }
1119
1120    /**
1121     * Return true if the character at specified position is a number
1122     *
1123     * @return bool
1124     *
1125     * @since 1.0.0
1126     */
1127    protected function isdigitat(string $str, int $pos) : bool
1128    {
1129        if ($pos >= \strlen($str)) {
1130            return false;
1131        }
1132
1133        return \ord($str[$pos]) >= \ord('0') && \ord($str[$pos]) <= \ord('9');
1134    }
1135
1136    /**
1137     * Return true if the character at specified position is an alphanumeric character
1138     *
1139     * @return bool
1140     *
1141     * @since 1.0.0
1142     */
1143    protected function isalnumat(string $str, int $pos) : bool
1144    {
1145        if ($pos >= \strlen($str)) {
1146            return false;
1147        }
1148
1149        return $this->lookAnTable(\ord($str[$pos])) >= 0;
1150    }
1151
1152    /**
1153     * identifyMode
1154     *
1155     * @return int
1156     *
1157     * @since 1.0.0
1158     */
1159    protected function identifyMode(int $pos) : int
1160    {
1161        if ($pos >= \strlen($this->dataStr)) {
1162            return self::QR_MODE_NL;
1163        }
1164
1165        $c = $this->dataStr[$pos];
1166        if ($this->isdigitat($this->dataStr, $pos)) {
1167            return self::QR_MODE_NM;
1168        } elseif ($this->isalnumat($this->dataStr, $pos)) {
1169            return self::QR_MODE_AN;
1170        } elseif ($this->hint === self::QR_MODE_KJ) {
1171            if ($pos + 1 < \strlen($this->dataStr)) {
1172                $d    = $this->dataStr[$pos + 1];
1173                $word = (\ord($c) << 8) | \ord($d);
1174
1175                if (($word >= 0x8140 && $word <= 0x9ffc)
1176                    || ($word >= 0xe040 && $word <= 0xebbf)
1177                ) {
1178                    return self::QR_MODE_KJ;
1179                }
1180            }
1181        }
1182
1183        return self::QR_MODE_8B;
1184    }
1185
1186    /**
1187     * eatNum
1188     *
1189     * @return int
1190     *
1191     * @since 1.0.0
1192     */
1193    protected function eatNum() : int
1194    {
1195        $ln = $this->lengthIndicator(self::QR_MODE_NM, $this->version);
1196        $p  = 0;
1197
1198        while ($this->isdigitat($this->dataStr, $p)) {
1199            ++$p;
1200        }
1201
1202        $run  = $p;
1203        $mode = $this->identifyMode($p);
1204
1205        if ($mode === self::QR_MODE_8B) {
1206            $dif = $this->estimateBitsModeNum($run) + 4 + $ln
1207                + $this->estimateBitsMode8(1)         // + 4 + l8
1208                - $this->estimateBitsMode8($run + 1); // - 4 - l8
1209
1210            if ($dif > 0) {
1211                return $this->eat8();
1212            }
1213        }
1214        if ($mode === self::QR_MODE_AN) {
1215            $dif = $this->estimateBitsModeNum($run) + 4 + $ln
1216                + $this->estimateBitsModeAn(1)        // + 4 + la
1217                - $this->estimateBitsModeAn($run + 1);// - 4 - la
1218
1219            if ($dif > 0) {
1220                return $this->eatAn();
1221            }
1222        }
1223
1224        $this->items = $this->appendNewInputItem($this->items, self::QR_MODE_NM, $run, \str_split($this->dataStr));
1225
1226        return $run;
1227    }
1228
1229    /**
1230     * eatAn
1231     *
1232     * @return int
1233     *
1234     * @since 1.0.0
1235     */
1236    protected function eatAn() : int
1237    {
1238        $la = $this->lengthIndicator(self::QR_MODE_AN,  $this->version);
1239        $ln = $this->lengthIndicator(self::QR_MODE_NM, $this->version);
1240        $p  = 1;
1241
1242        while ($this->isalnumat($this->dataStr, $p)) {
1243            if ($this->isdigitat($this->dataStr, $p)) {
1244                $q = $p;
1245                while ($this->isdigitat($this->dataStr, $q)) {
1246                    ++$q;
1247                }
1248
1249                $dif = $this->estimateBitsModeAn($p) // + 4 + la
1250                    + $this->estimateBitsModeNum($q - $p) + 4 + $ln
1251                    - $this->estimateBitsModeAn($q); // - 4 - la
1252
1253                if ($dif < 0) {
1254                    break;
1255                } else {
1256                    $p = $q;
1257                }
1258            } else {
1259                ++$p;
1260            }
1261        }
1262
1263        $run = $p;
1264        if (!$this->isalnumat($this->dataStr, $p)) {
1265            $dif = $this->estimateBitsModeAn($run) + 4 + $la
1266                + $this->estimateBitsMode8(1) // + 4 + l8
1267                - $this->estimateBitsMode8($run + 1); // - 4 - l8
1268
1269            if ($dif > 0) {
1270                return $this->eat8();
1271            }
1272        }
1273
1274        $this->items = $this->appendNewInputItem($this->items, self::QR_MODE_AN, $run, \str_split($this->dataStr));
1275
1276        return $run;
1277    }
1278
1279    /**
1280     * eatKanji
1281     *
1282     * @return int
1283     *
1284     * @since 1.0.0
1285     */
1286    protected function eatKanji() : int
1287    {
1288        $p = 0;
1289        while ($this->identifyMode($p) === self::QR_MODE_KJ) {
1290            $p += 2;
1291        }
1292
1293        $this->items = $this->appendNewInputItem($this->items, self::QR_MODE_KJ, $p, \str_split($this->dataStr));
1294
1295        return $p;
1296    }
1297
1298    /**
1299     * eat8
1300     *
1301     * @return int
1302     *
1303     * @since 1.0.0
1304     */
1305    protected function eat8() : int
1306    {
1307        $la         = $this->lengthIndicator(self::QR_MODE_AN, $this->version);
1308        $ln         = $this->lengthIndicator(self::QR_MODE_NM, $this->version);
1309        $p          = 1;
1310        $dataStrLen = \strlen($this->dataStr);
1311
1312        while ($p < $dataStrLen) {
1313            $mode = $this->identifyMode($p);
1314            if ($mode === self::QR_MODE_KJ) {
1315                break;
1316            }
1317
1318            if ($mode === self::QR_MODE_NM) {
1319                $q = $p;
1320                while ($this->isdigitat($this->dataStr, $q)) {
1321                    ++$q;
1322                }
1323
1324                $dif = $this->estimateBitsMode8($p) // + 4 + l8
1325                    + $this->estimateBitsModeNum($q - $p) + 4 + $ln
1326                    - $this->estimateBitsMode8($q); // - 4 - l8
1327
1328                if ($dif < 0) {
1329                    break;
1330                } else {
1331                    $p = $q;
1332                }
1333            } elseif ($mode === self::QR_MODE_AN) {
1334                $q = $p;
1335                while ($this->isalnumat($this->dataStr, $q)) {
1336                    ++$q;
1337                }
1338
1339                $dif = $this->estimateBitsMode8($p)  // + 4 + l8
1340                    + $this->estimateBitsModeAn($q - $p) + 4 + $la
1341                    - $this->estimateBitsMode8($q); // - 4 - l8
1342
1343                if ($dif < 0) {
1344                    break;
1345                } else {
1346                    $p = $q;
1347                }
1348            } else {
1349                ++$p;
1350            }
1351        }
1352
1353        $run         = $p;
1354        $this->items = $this->appendNewInputItem($this->items, self::QR_MODE_8B, $run, \str_split($this->dataStr));
1355
1356        return $run;
1357    }
1358
1359    /**
1360     * splitString
1361     *
1362     * @return int
1363     *
1364     * @since 1.0.0
1365     */
1366    protected function splitString() : int
1367    {
1368        while (\strlen($this->dataStr) > 0) {
1369            $mode = $this->identifyMode(0);
1370            switch ($mode) {
1371                case self::QR_MODE_NM:
1372                    $length = $this->eatNum();
1373                    break;
1374                case self::QR_MODE_AN:
1375                    $length = $this->eatAn();
1376                    break;
1377                case self::QR_MODE_KJ:
1378                    $length = $this->hint === self::QR_MODE_KJ
1379                        ? $this->eatKanji()
1380                        : $this->eat8();
1381                    break;
1382                default:
1383                    $length = $this->eat8();
1384                    break;
1385            }
1386
1387            if ($length === 0) {
1388                return 0;
1389            }
1390
1391            if ($length < 0) {
1392                return -1;
1393            }
1394
1395            $this->dataStr = \substr($this->dataStr, $length);
1396        }
1397
1398        return 0;
1399    }
1400
1401    /**
1402     * toUpper
1403     *
1404     * @return string
1405     *
1406     * @since 1.0.0
1407     */
1408    protected function toUpper() : string
1409    {
1410        $stringLen = \strlen($this->dataStr);
1411        $p         = 0;
1412
1413        while ($p < $stringLen) {
1414            $mode = $this->identifyMode(\strlen(\substr($this->dataStr, $p))/*, $this->hint*/);
1415
1416            if ($mode === self::QR_MODE_KJ) {
1417                $p += 2;
1418            } else {
1419                if (\ord($this->dataStr[$p]) >= \ord('a')
1420                    && \ord($this->dataStr[$p]) <= \ord('z')
1421                ) {
1422                    $this->dataStr[$p] = \chr(\ord($this->dataStr[$p]) - 32);
1423                }
1424
1425                ++$p;
1426            }
1427        }
1428        return $this->dataStr;
1429    }
1430
1431    /**
1432     * newInputItem
1433     *
1434     * @return array
1435     *
1436     * @since 1.0.0
1437     */
1438    protected function newInputItem(int $mode, int $size, array $data, array $bstream = []) : array
1439    {
1440        $setData = \array_slice($data, 0, $size);
1441        if (\count($setData) < $size) {
1442            $setData = \array_merge($setData, \array_fill(0, ($size - \count($setData)), 0));
1443        }
1444
1445        if (!$this->check($mode, $size, $setData)) {
1446            return [];
1447        }
1448
1449        $inputitem            = [];
1450        $inputitem['mode']    = $mode;
1451        $inputitem['size']    = $size;
1452        $inputitem['data']    = $setData;
1453        $inputitem['bstream'] = $bstream;
1454
1455        return $inputitem;
1456    }
1457
1458    /**
1459     * encodeModeNum
1460     *
1461     * @return array
1462     *
1463     * @since 1.0.0
1464     */
1465    protected function encodeModeNum(array $inputitem, int $version) : array
1466    {
1467        $words                = (int) ($inputitem['size'] / 3);
1468        $inputitem['bstream'] = [];
1469        $val                  = 0x1;
1470        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1471        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(self::QR_MODE_NM, $version), $inputitem['size']);
1472
1473        $ord0 = \ord('0');
1474
1475        for ($i = 0; $i < $words; ++$i) {
1476            $val  = (\ord($inputitem['data'][$i * 3  ]) - $ord0) * 100;
1477            $val += (\ord($inputitem['data'][$i * 3 + 1]) - $ord0) * 10;
1478            $val += (\ord($inputitem['data'][$i * 3 + 2]) - $ord0);
1479
1480            $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 10, $val);
1481        }
1482
1483        if ($inputitem['size'] - $words * 3 === 1) {
1484            $val                  = \ord($inputitem['data'][$words * 3]) - $ord0;
1485            $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val);
1486        } elseif (($inputitem['size'] - ($words * 3)) === 2) {
1487            $val  = (\ord($inputitem['data'][$words * 3  ]) - $ord0) * 10;
1488            $val += (\ord($inputitem['data'][$words * 3 + 1]) - $ord0);
1489
1490            $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 7, $val);
1491        }
1492
1493        return $inputitem;
1494    }
1495
1496    /**
1497     * encodeModeAn
1498     *
1499     * @return array
1500     *
1501     * @since 1.0.0
1502     */
1503    protected function encodeModeAn(array $inputitem, int $version) : array
1504    {
1505        $words = (int) ($inputitem['size'] / 2);
1506
1507        $inputitem['bstream'] = [];
1508        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x02);
1509        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(self::QR_MODE_AN, $version), $inputitem['size']);
1510
1511        for ($i = 0; $i < $words; ++$i) {
1512            $val  = $this->lookAnTable(\ord($inputitem['data'][$i * 2])) * 45;
1513            $val += $this->lookAnTable(\ord($inputitem['data'][($i * 2) + 1]));
1514
1515            $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 11, $val);
1516        }
1517
1518        if (($inputitem['size'] & 1) !== 0) {
1519            $val = $this->lookAnTable(\ord($inputitem['data'][($words * 2)]));
1520
1521            $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 6, $val);
1522        }
1523
1524        return $inputitem;
1525    }
1526
1527    /**
1528     * encodeMode8
1529     *
1530     * @return array
1531     *
1532     * @since 1.0.0
1533     */
1534    protected function encodeMode8(array $inputitem, int $version) : array
1535    {
1536        $inputitem['bstream'] = [];
1537        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x4);
1538        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(self::QR_MODE_8B, $version), $inputitem['size']);
1539
1540        for ($i=0; $i < $inputitem['size']; ++$i) {
1541            $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, \ord($inputitem['data'][$i]));
1542        }
1543
1544        return $inputitem;
1545    }
1546
1547    /**
1548     * encodeModeKanji
1549     *
1550     * @return array
1551     *
1552     * @since 1.0.0
1553     */
1554    protected function encodeModeKanji(array $inputitem, int $version) : array
1555    {
1556        $inputitem['bstream'] = [];
1557        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x8);
1558        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(self::QR_MODE_KJ, $version), (int) ($inputitem['size'] / 2));
1559
1560        for ($i = 0; $i < $inputitem['size']; $i += 2) {
1561            $val = (\ord($inputitem['data'][$i]) << 8) | \ord($inputitem['data'][$i + 1]);
1562
1563            if ($val <= 0x9ffc) {
1564                $val -= 0x8140;
1565            } else {
1566                $val -= 0xc140;
1567            }
1568
1569            $h   = ($val >> 8) * 0xc0;
1570            $val = ($val & 0xff) + $h;
1571
1572            $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 13, $val);
1573        }
1574
1575        return $inputitem;
1576    }
1577
1578    /**
1579     * encodeModeStructure
1580     *
1581     * @return array
1582     *
1583     * @since 1.0.0
1584     */
1585    protected function encodeModeStructure(array $inputitem) : array
1586    {
1587        $inputitem['bstream'] = [];
1588        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x03);
1589        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, \ord($inputitem['data'][1]) - 1);
1590        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, \ord($inputitem['data'][0]) - 1);
1591        $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, \ord($inputitem['data'][2]));
1592
1593        return $inputitem;
1594    }
1595
1596    /**
1597     * encodeBitStream
1598     *
1599     * @return array
1600     *
1601     * @since 1.0.0
1602     */
1603    protected function encodeBitStream(array $inputitem, int $version) : array
1604    {
1605        $inputitem['bstream'] = [];
1606        $words                = $this->maximumWords($inputitem['mode'], $version);
1607
1608        if ($inputitem['size'] > $words) {
1609            $st1 = $this->newInputItem($inputitem['mode'], $words, $inputitem['data']);
1610            $st2 = $this->newInputItem($inputitem['mode'], $inputitem['size'] - $words, \array_slice($inputitem['data'], $words));
1611            $st1 = $this->encodeBitStream($st1, $version);
1612            $st2 = $this->encodeBitStream($st2, $version);
1613
1614            $inputitem['bstream'] = [];
1615            $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st1['bstream']);
1616            $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st2['bstream']);
1617        } else {
1618            switch($inputitem['mode']) {
1619                case self::QR_MODE_NM:
1620                    $inputitem = $this->encodeModeNum($inputitem, $version);
1621                    break;
1622                case self::QR_MODE_AN:
1623                    $inputitem = $this->encodeModeAn($inputitem, $version);
1624                    break;
1625                case self::QR_MODE_8B:
1626                    $inputitem = $this->encodeMode8($inputitem, $version);
1627                    break;
1628                case self::QR_MODE_KJ:
1629                    $inputitem = $this->encodeModeKanji($inputitem, $version);
1630                    break;
1631                case self::QR_MODE_ST:
1632                    $inputitem = $this->encodeModeStructure($inputitem);
1633                    break;
1634            }
1635        }
1636
1637        return $inputitem;
1638    }
1639
1640    /**
1641     * Append data to an input object.
1642     * The data is copied and appended to the input object.
1643     *
1644     * @return array
1645     *
1646     * @since 1.0.0
1647     */
1648    protected function appendNewInputItem(array $items, int $mode, int $size, array $data) : array
1649    {
1650        $newitem = $this->newInputItem($mode, $size, $data);
1651
1652        if (!empty($newitem)) {
1653            $items[] = $newitem;
1654        }
1655
1656        return $items;
1657    }
1658
1659    /**
1660     * insertStructuredAppendHeader
1661     *
1662     * @return array
1663     *
1664     * @since 1.0.0
1665     */
1666    protected function insertStructuredAppendHeader(array $items, int $size, int $index, int $parity) : array
1667    {
1668        if ($size > self::MAX_STRUCTURED_SYMBOLS
1669            || $index <= 0
1670            || $index > self::MAX_STRUCTURED_SYMBOLS
1671        ) {
1672            return [];
1673        }
1674
1675        $buf   = [$size, $index, $parity];
1676        $entry = $this->newInputItem(self::QR_MODE_ST, 3, $buf);
1677
1678        \array_unshift($items, $entry);
1679
1680        return $items;
1681    }
1682
1683    /**
1684     * calcParity
1685     *
1686     * @return int
1687     *
1688     * @since 1.0.0
1689     */
1690    protected function calcParity(array $items) : int
1691    {
1692        $parity = 0;
1693        foreach ($items as $item) {
1694            if ($item['mode'] !== self::QR_MODE_ST) {
1695                for ($i = $item['size'] - 1; $i >= 0; --$i) {
1696                    $parity ^= $item['data'][$i];
1697                }
1698            }
1699        }
1700
1701        return $parity;
1702    }
1703
1704    /**
1705     * checkModeNum
1706     *
1707     * @return bool
1708     *
1709     * @since 1.0.0
1710     */
1711    protected function checkModeNum(int $size, array $data) : bool
1712    {
1713        for ($i = 0; $i < $size; ++$i) {
1714            if (\ord($data[$i]) < \ord('0') || \ord($data[$i]) > \ord('9')) {
1715                return false;
1716            }
1717        }
1718
1719        return true;
1720    }
1721
1722    /**
1723     * Look up the alphabet-numeric conversion table (see JIS X0510:2004, pp.19).
1724     *
1725     * @return int
1726     *
1727     * @since 1.0.0
1728     */
1729    protected function lookAnTable(int $c) : int
1730    {
1731        return $c > 127 ? -1 : self::AN_TABLE[$c];
1732    }
1733
1734    /**
1735     * checkModeAn
1736     *
1737     * @return bool
1738     *
1739     * @since 1.0.0
1740     */
1741    protected function checkModeAn(int $size, array $data) : bool
1742    {
1743        for ($i = 0; $i < $size; ++$i) {
1744            if ($this->lookAnTable(\ord($data[$i])) === -1) {
1745                return false;
1746            }
1747        }
1748
1749        return true;
1750    }
1751
1752    /**
1753     * estimateBitsModeNum
1754     *
1755     * @return int
1756     *
1757     * @since 1.0.0
1758     */
1759    protected function estimateBitsModeNum(int $size) : int
1760    {
1761        $w    = (int) ($size / 3);
1762        $bits = ($w * 10);
1763
1764        switch($size - ($w * 3)) {
1765            case 1:
1766                $bits += 4;
1767                break;
1768            case 2:
1769                $bits += 7;
1770                break;
1771        }
1772
1773        return $bits;
1774    }
1775
1776    /**
1777     * estimateBitsModeAn
1778     *
1779     * @return int
1780     *
1781     * @since 1.0.0
1782     */
1783    protected function estimateBitsModeAn(int $size) : int
1784    {
1785        $bits = (int) ($size * 5.5); // (size / 2 ) * 11
1786        if (($size & 1) !== 0) {
1787            $bits += 6;
1788        }
1789
1790        return $bits;
1791    }
1792
1793    /**
1794     * estimateBitsMode8
1795     *
1796     * @return int
1797     *
1798     * @since 1.0.0
1799     */
1800    protected function estimateBitsMode8(int $size) : int
1801    {
1802        return $size * 8;
1803    }
1804
1805    /**
1806     * estimateBitsModeKanji
1807     *
1808     * @return int
1809     *
1810     * @since 1.0.0
1811     */
1812    protected function estimateBitsModeKanji(int $size) : int
1813    {
1814        return (int) ($size * 6.5); // (size / 2 ) * 13
1815    }
1816
1817    /**
1818     * checkModeKanji
1819     *
1820     * @return bool
1821     *
1822     * @since 1.0.0
1823     */
1824    protected function checkModeKanji(int $size, array $data) : bool
1825    {
1826        if (($size & 1) !== 0) {
1827            return false;
1828        }
1829
1830        for ($i = 0; $i < $size; $i += 2) {
1831            $val = (\ord($data[$i]) << 8) | \ord($data[$i + 1]);
1832            if ($val < 0x8140 || ($val > 0x9ffc && $val < 0xe040) || $val > 0xebbf) {
1833                return false;
1834            }
1835        }
1836
1837        return true;
1838    }
1839
1840    /**
1841     * Validate the input data.
1842     *
1843     * @return bool
1844     *
1845     * @since 1.0.0
1846     */
1847    protected function check(int $mode, int $size, array $data) : bool
1848    {
1849        if ($size <= 0) {
1850            return false;
1851        }
1852
1853        switch($mode) {
1854            case self::QR_MODE_NM:
1855                return $this->checkModeNum($size, $data);
1856            case self::QR_MODE_AN:
1857                return $this->checkModeAn($size, $data);
1858            case self::QR_MODE_KJ:
1859                return $this->checkModeKanji($size, $data);
1860            case self::QR_MODE_8B:
1861                return true;
1862            case self::QR_MODE_ST:
1863                return true;
1864        }
1865
1866        return false;
1867    }
1868
1869    /**
1870     * estimateBitStreamSize
1871     *
1872     * @return int
1873     *
1874     * @since 1.0.0
1875     */
1876    protected function estimateBitStreamSize(array $items, int $version) : int
1877    {
1878        $bits = 0;
1879        if ($version === 0) {
1880            $version = 1;
1881        }
1882
1883        foreach ($items as $item) {
1884            switch($item['mode']) {
1885                case self::QR_MODE_NM:
1886                    $bits = $this->estimateBitsModeNum($item['size']);
1887                    break;
1888                case self::QR_MODE_AN:
1889                    $bits = $this->estimateBitsModeAn($item['size']);
1890                    break;
1891                case self::QR_MODE_8B:
1892                    $bits = $this->estimateBitsMode8($item['size']);
1893                    break;
1894                case self::QR_MODE_KJ:
1895                    $bits = $this->estimateBitsModeKanji($item['size']);
1896                    break;
1897                case self::QR_MODE_ST:
1898                    return self::STRUCTURE_HEADER_BITS;
1899                default:
1900                    return 0;
1901            }
1902
1903            $l     = $this->lengthIndicator($item['mode'], $version);
1904            $m     = 1 << $l;
1905            $num   = (int) (($item['size'] + $m - 1) / $m);
1906            $bits += $num * (4 + $l);
1907        }
1908
1909        return $bits;
1910    }
1911
1912    /**
1913     * estimateVersion
1914     *
1915     * @return int
1916     *
1917     * @since 1.0.0
1918     */
1919    protected function estimateVersion(array $items) : int
1920    {
1921        $version = 0;
1922        $prev    = 0;
1923
1924        do {
1925            $prev    = $version;
1926            $bits    = $this->estimateBitStreamSize($items, $prev);
1927            $version = $this->getMinimumVersion((int) (($bits + 7) / 8), $this->level);
1928
1929            if ($version < 0) {
1930                return -1;
1931            }
1932        } while ($version > $prev);
1933
1934        return $version;
1935    }
1936
1937    /**
1938     * lengthOfCode
1939     *
1940     * @return int
1941     *
1942     * @since 1.0.0
1943     */
1944    protected function lengthOfCode(int $mode, int $version, int $bits) : int
1945    {
1946        $payload = $bits - 4 - $this->lengthIndicator($mode, $version);
1947        switch($mode) {
1948            case self::QR_MODE_NM:
1949                $chunks = (int) ($payload / 10);
1950                $remain = $payload - $chunks * 10;
1951                $size   = $chunks * 3;
1952
1953                if ($remain >= 7) {
1954                    $size += 2;
1955                } elseif ($remain >= 4) {
1956                    ++$size;
1957                }
1958
1959                break;
1960            case self::QR_MODE_AN:
1961                $chunks = (int) ($payload / 11);
1962                $remain = $payload - $chunks * 11;
1963                $size   = $chunks * 2;
1964
1965                if ($remain >= 6) {
1966                    ++$size;
1967                }
1968
1969                break;
1970            case self::QR_MODE_8B:
1971                $size = (int) ($payload / 8);
1972                break;
1973            case self::QR_MODE_KJ:
1974                $size = (int) (($payload / 13) * 2);
1975                break;
1976            case self::QR_MODE_ST:
1977                $size = (int) ($payload / 8);
1978                break;
1979            default:
1980                $size = 0;
1981                break;
1982        }
1983
1984        $maxsize = $this->maximumWords($mode, $version);
1985        if ($size < 0) {
1986            $size = 0;
1987        }
1988
1989        if ($size > $maxsize) {
1990            $size = $maxsize;
1991        }
1992
1993        return $size;
1994    }
1995
1996    /**
1997     * createBitStream
1998     *
1999     * @return array
2000     *
2001     * @since 1.0.0
2002     */
2003    protected function createBitStream(array $items) : array
2004    {
2005        $total = 0;
2006        foreach ($items as $key => $item) {
2007            $items[$key] = $this->encodeBitStream($item, $this->version);
2008            $bits        = \count($items[$key]['bstream']);
2009            $total      += $bits;
2010        }
2011
2012        return [$items, $total];
2013    }
2014
2015    /**
2016     * convertData
2017     *
2018     * @return array
2019     *
2020     * @since 1.0.0
2021     */
2022    protected function convertData(array $items) : array
2023    {
2024        $ver = $this->estimateVersion($items);
2025        if ($ver > $this->version) {
2026            $this->version = $ver;
2027        }
2028
2029        while (true) {
2030            $cbs   = $this->createBitStream($items);
2031            $items = $cbs[0];
2032            $bits  = $cbs[1];
2033
2034            if ($bits < 0) {
2035                return [];
2036            }
2037
2038            $ver = $this->getMinimumVersion((int) (($bits + 7) / 8), $this->level);
2039            if ($ver < 0) {
2040                return [];
2041            } elseif ($ver > $this->version) {
2042                $this->version = $ver;
2043            } else {
2044                break;
2045            }
2046        }
2047
2048        return $items;
2049    }
2050
2051    /**
2052     * Append Padding Bit to bitstream
2053     *
2054     * @return array
2055     *
2056     * @since 1.0.0
2057     */
2058    protected function appendPaddingBit(array $bstream) : array
2059    {
2060        if (empty($bstream)) {
2061            return [];
2062        }
2063
2064        $bits     = \count($bstream);
2065        $maxwords = self::CAPACITY[$this->version][self::QRCAP_WORDS] - self::CAPACITY[$this->version][self::QRCAP_EC][$this->level];
2066        $maxbits  = $maxwords * 8;
2067
2068        if ($maxbits === $bits) {
2069            return $bstream;
2070        }
2071
2072        if ($maxbits - $bits < 5) {
2073            return $this->appendNum($bstream, $maxbits - $bits, 0);
2074        }
2075
2076        $bits   += 4;
2077        $words   = (int) (($bits + 7) / 8);
2078        $padding = [];
2079        $padding = $this->appendNum($padding, $words * 8 - $bits + 4, 0);
2080        $padlen  = $maxwords - $words;
2081
2082        if ($padlen > 0) {
2083            $padbuf = [];
2084
2085            for ($i = 0; $i < $padlen; ++$i) {
2086                $padbuf[$i] = (($i & 1) !== 0) ? 0x11 : 0xec;
2087            }
2088
2089            $padding = $this->appendBytes($padding, $padlen, $padbuf);
2090        }
2091
2092        return $this->appendBitstream($bstream, $padding);
2093    }
2094
2095    /**
2096     * mergeBitStream
2097     *
2098     * @return array
2099     *
2100     * @since 1.0.0
2101     */
2102    protected function mergeBitStream(array $items) : array
2103    {
2104        $items = $this->convertData($items);
2105        if (empty($items)) {
2106            return [];
2107        }
2108
2109        $bstream = [];
2110        foreach ($items as $item) {
2111            $bstream = $this->appendBitstream($bstream, $item['bstream']);
2112        }
2113
2114        return $bstream;
2115    }
2116
2117    /**
2118     * Returns a stream of bits.
2119     *
2120     * @return array
2121     *
2122     * @since 1.0.0
2123     */
2124    protected function getBitStream(array $items) : array
2125    {
2126        $bstream = $this->mergeBitStream($items);
2127
2128        return $this->appendPaddingBit($bstream);
2129    }
2130
2131    /**
2132     * Pack all bit streams padding bits into a byte array.
2133     *
2134     * @return array
2135     *
2136     * @since 1.0.0
2137     */
2138    protected function getByteStream(array $items) : array
2139    {
2140        $bstream = $this->getBitStream($items);
2141
2142        return $this->bitstreamToByte($bstream);
2143    }
2144
2145    /**
2146     * Return an array with zeros
2147     *
2148     * @return array
2149     *
2150     * @since 1.0.0
2151     */
2152    protected function allocate(int $setLength) : array
2153    {
2154        return \array_fill(0, $setLength, 0);
2155    }
2156
2157    /**
2158     * Return new bitstream from number
2159     *
2160     * @return array
2161     *
2162     * @since 1.0.0
2163     */
2164    protected function newFromNum(int $bits, int $num) : array
2165    {
2166        $bstream = $this->allocate($bits);
2167        $mask    = 1 << ($bits - 1);
2168
2169        for ($i = 0; $i < $bits; ++$i) {
2170            $bstream[$i] = (($num & $mask) !== 0) ? 1 : 0;
2171            $mask      >>= 1;
2172        }
2173
2174        return $bstream;
2175    }
2176
2177    /**
2178     * Return new bitstream from bytes
2179     *
2180     * @return array
2181     *
2182     * @since 1.0.0
2183     */
2184    protected function newFromBytes(int $size, array $data) : array
2185    {
2186        $bstream = $this->allocate($size * 8);
2187        $p       = 0;
2188
2189        for ($i = 0; $i < $size; ++$i) {
2190            $mask = 0x80;
2191
2192            for ($j = 0; $j < 8; ++$j) {
2193                $bstream[$p] = (($data[$i] & $mask) !== 0) ? 1 : 0;
2194                     $mask >>= 1;
2195
2196                ++$p;
2197            }
2198        }
2199
2200        return $bstream;
2201    }
2202
2203    /**
2204     * Append one bitstream to another
2205     *
2206     * @return array
2207     *
2208     * @since 1.0.0
2209     */
2210    protected function appendBitstream(array $bitstream, array $append) : array
2211    {
2212        if (empty($append)) {
2213            return $bitstream;
2214        }
2215
2216        if (empty($bitstream)) {
2217            return $append;
2218        }
2219
2220        return \array_values(\array_merge($bitstream, $append));
2221    }
2222
2223    /**
2224     * Append one bitstream created from number to another
2225     *
2226     * @return array
2227     *
2228     * @since 1.0.0
2229     */
2230    protected function appendNum(array $bitstream, int $bits, int $num) : array
2231    {
2232        if ($bits === 0) {
2233            return [];
2234        }
2235
2236        $b = $this->newFromNum($bits, $num);
2237
2238        return $this->appendBitstream($bitstream, $b);
2239    }
2240
2241    /**
2242     * Append one bitstream created from bytes to another
2243     *
2244     * @return array
2245     *
2246     * @since 1.0.0
2247     */
2248    protected function appendBytes(array $bitstream, int $size, array $data) : array
2249    {
2250        if ($size === 0) {
2251            return [];
2252        }
2253
2254        $b = $this->newFromBytes($size, $data);
2255
2256        return $this->appendBitstream($bitstream, $b);
2257    }
2258
2259    /**
2260     * Convert bitstream to bytes
2261     *
2262     * @return array
2263     *
2264     * @since 1.0.0
2265     */
2266    protected function bitstreamToByte(array $bstream) : array
2267    {
2268        $size = \count($bstream);
2269        if ($size === 0) {
2270            return [];
2271        }
2272
2273        $data  = \array_fill(0, (int) (($size + 7) / 8), 0);
2274        $bytes = (int) ($size / 8);
2275        $p     = 0;
2276
2277        for ($i = 0; $i < $bytes; ++$i) {
2278            $v = 0;
2279
2280            for ($j = 0; $j < 8; ++$j) {
2281                $v <<= 1;
2282                $v  |= $bstream[$p];
2283                ++$p;
2284            }
2285
2286            $data[$i] = $v;
2287        }
2288
2289        if (($size & 7) !== 0) {
2290            $v = 0;
2291
2292            for ($j = 0; $j < ($size & 7); ++$j) {
2293                $v <<= 1;
2294                $v  |= $bstream[$p];
2295                ++$p;
2296            }
2297
2298            $data[$bytes] = $v;
2299        }
2300
2301        return $data;
2302    }
2303
2304    /**
2305     * Replace a value on the array at the specified position
2306     *
2307     * @return array
2308     *
2309     * @since 1.0.0
2310     */
2311    protected function qrstrset(array $srctab, int $x, int $y, string $repl, int $replLen = 0) : array
2312    {
2313        $srctab[$y] = \substr_replace(
2314            $srctab[$y],
2315            $replLen !== 0 ? \substr($repl, 0, $replLen) : $repl,
2316            $x,
2317            $replLen !== 0 ? $replLen : \strlen($repl)
2318        );
2319
2320        return $srctab;
2321    }
2322
2323    /**
2324     * Return a version number that satisfies the input code length.
2325     *
2326     * @return int
2327     *
2328     * @since 1.0.0
2329     */
2330    protected function getMinimumVersion(int $size, int $level) : int
2331    {
2332        for ($i = 1; $i <= self::QRSPEC_VERSION_MAX; ++$i) {
2333            $words = self::CAPACITY[$i][self::QRCAP_WORDS] - self::CAPACITY[$i][self::QRCAP_EC][$level];
2334            if ($words >= $size) {
2335                return $i;
2336            }
2337        }
2338
2339        // the size of input data is greater than QR capacity, try to lover the error correction mode
2340        return -1;
2341    }
2342
2343    /**
2344     * Return the size of length indicator for the mode and version.
2345     *
2346     * @return int
2347     *
2348     * @since 1.0.0
2349     */
2350    protected function lengthIndicator(int $mode, int $version) : int
2351    {
2352        if ($mode === self::QR_MODE_ST) {
2353            return 0;
2354        }
2355
2356        if ($version <= 9) {
2357            $l = 0;
2358        } elseif ($version <= 26) {
2359            $l = 1;
2360        } else {
2361            $l = 2;
2362        }
2363
2364        return self::LENGTH_TABLE_BITS[$mode][$l];
2365    }
2366
2367    /**
2368     * Return the maximum length for the mode and version.
2369     *
2370     * @return int
2371     *
2372     * @since 1.0.0
2373     */
2374    protected function maximumWords(int $mode, int $version) : int
2375    {
2376        if ($mode === self::QR_MODE_ST) {
2377            return 3;
2378        }
2379
2380        if ($version <= 9) {
2381            $l = 0;
2382        } elseif ($version <= 26) {
2383            $l = 1;
2384        } else {
2385            $l = 2;
2386        }
2387
2388        $bits  = self::LENGTH_TABLE_BITS[$mode][$l];
2389        $words = (1 << $bits) - 1;
2390
2391        if ($mode === self::QR_MODE_KJ) {
2392            $words *= 2; // the number of bytes is required
2393        }
2394
2395        return $words;
2396    }
2397
2398    /**
2399     * Return an array of ECC specification.
2400     *
2401     * @return array
2402     *
2403     * @since 1.0.0
2404     */
2405    protected function getEccSpec(int $version, int $level, array $spec) : array
2406    {
2407        if (\count($spec) < 5) {
2408            $spec = [0, 0, 0, 0, 0];
2409        }
2410
2411        $b1   = self::ECC_TABLE[$version][$level][0];
2412        $b2   = self::ECC_TABLE[$version][$level][1];
2413        $data = self::CAPACITY[$version][self::QRCAP_WORDS] - self::CAPACITY[$version][self::QRCAP_EC][$level];
2414        $ecc  = self::CAPACITY[$version][self::QRCAP_EC][$level];
2415
2416        if ($b2 === 0) {
2417            $spec[0] = $b1;
2418            $spec[1] = (int) ($data / $b1);
2419            $spec[2] = (int) ($ecc / $b1);
2420            $spec[3] = 0;
2421            $spec[4] = 0;
2422        } else {
2423            $spec[0] = $b1;
2424            $spec[1] = (int) ($data / ($b1 + $b2));
2425            $spec[2] = (int) ($ecc / ($b1 + $b2));
2426            $spec[3] = $b2;
2427            $spec[4] = $spec[1] + 1;
2428        }
2429
2430        return $spec;
2431    }
2432
2433    /**
2434     * Put an alignment marker.
2435     *
2436     * @return array
2437     *
2438     * @since 1.0.0
2439     */
2440    protected function putAlignmentMarker(array $frame, int $ox, int $oy) : array
2441    {
2442        $yStart = $oy - 2;
2443        $xStart = $ox - 2;
2444
2445        for ($y = 0; $y < 5; ++$y) {
2446            $frame = $this->qrstrset($frame, $xStart, $yStart + $y, self::PUT_ALIGNMENT_FILTER[$y]);
2447        }
2448
2449        return $frame;
2450    }
2451
2452    /**
2453     * Put an alignment pattern.
2454     *
2455     * @return array
2456     *
2457     * @since 1.0.0
2458     */
2459    protected function putAlignmentPattern(int $version, array $frame, int $width) : array
2460    {
2461        if ($version < 2) {
2462            return $frame;
2463        }
2464
2465        $d = self::ALIGNMENT_PATTERN[$version][1] - self::ALIGNMENT_PATTERN[$version][0];
2466        $w = $d < 0
2467            ? 2
2468            : (int) (($width - self::ALIGNMENT_PATTERN[$version][0]) / $d + 2);
2469
2470        if ($w * $w - 3 === 1) {
2471            $x = self::ALIGNMENT_PATTERN[$version][0];
2472            $y = self::ALIGNMENT_PATTERN[$version][0];
2473
2474            return $this->putAlignmentMarker($frame, $x, $y);
2475        }
2476
2477        $cx = self::ALIGNMENT_PATTERN[$version][0];
2478        $wo = $w - 1;
2479
2480        for ($x = 1; $x < $wo; ++$x) {
2481            $frame = $this->putAlignmentMarker($frame, 6, $cx);
2482            $frame = $this->putAlignmentMarker($frame, $cx,  6);
2483
2484            $cx += $d;
2485        }
2486
2487        $cy = self::ALIGNMENT_PATTERN[$version][0];
2488        for ($y = 0; $y < $wo; ++$y) {
2489            $cx = self::ALIGNMENT_PATTERN[$version][0];
2490
2491            for ($x = 0; $x < $wo; ++$x) {
2492                $frame = $this->putAlignmentMarker($frame, $cx, $cy);
2493
2494                $cx += $d;
2495            }
2496
2497            $cy += $d;
2498        }
2499
2500        return $frame;
2501    }
2502
2503    /**
2504     * Put a finder pattern.
2505     *
2506     * @return array
2507     *
2508     * @since 1.0.0
2509     */
2510    protected function putFinderPattern(array $frame, int $ox, int $oy) : array
2511    {
2512        for ($y = 0; $y < 7; ++$y) {
2513            $frame = $this->qrstrset($frame, $ox, ($oy + $y), self::PUT_FINDER[$y]);
2514        }
2515
2516        return $frame;
2517    }
2518
2519    /**
2520     * Return a copy of initialized frame.
2521     *
2522     * @return array
2523     *
2524     * @since 1.0.0
2525     */
2526    protected function createFrame(int $version) : array
2527    {
2528        $width     = self::CAPACITY[$version][self::QRCAP_WIDTH];
2529        $frameLine = \str_repeat("\0", $width);
2530        $frame     = \array_fill(0, $width, $frameLine);
2531
2532        // Finder pattern
2533        $frame = $this->putFinderPattern($frame, 0, 0);
2534        $frame = $this->putFinderPattern($frame, $width - 7, 0);
2535        $frame = $this->putFinderPattern($frame, 0, $width - 7);
2536
2537        // Separator
2538        $yOffset = $width - 7;
2539        for ($y = 0; $y < 7; ++$y) {
2540            $frame[$y][7]          = "\xc0";
2541            $frame[$y][$width - 8] = "\xc0";
2542            $frame[$yOffset][7]    = "\xc0";
2543
2544            ++$yOffset;
2545        }
2546
2547        $setPattern = \str_repeat("\xc0", 8);
2548        $frame      = $this->qrstrset($frame, 0, 7, $setPattern);
2549        $frame      = $this->qrstrset($frame, $width - 8, 7, $setPattern);
2550        $frame      = $this->qrstrset($frame, 0, $width - 8, $setPattern);
2551
2552        // Format info
2553        $setPattern = \str_repeat("\x84", 9);
2554        $frame      = $this->qrstrset($frame, 0, 8, $setPattern);
2555        $frame      = $this->qrstrset($frame, $width - 8, 8, $setPattern, 8);
2556        $yOffset    = $width - 8;
2557
2558        for ($y = 0; $y < 8; ++$y, ++$yOffset) {
2559            $frame[$y][8]       = "\x84";
2560            $frame[$yOffset][8] = "\x84";
2561        }
2562
2563        // Timing pattern
2564        $wo = $width - 15;
2565        for ($i = 1; $i < $wo; ++$i) {
2566            $frame[6][7 + $i] = \chr(0x90 | ($i & 1));
2567            $frame[7 + $i][6] = \chr(0x90 | ($i & 1));
2568        }
2569
2570        // Alignment pattern
2571        $frame = $this->putAlignmentPattern($version, $frame, $width);
2572
2573        // Version information
2574        if ($version >= 7) {
2575            $vinf = $version > self::QRSPEC_VERSION_MAX
2576                ? 0
2577                : self::VERSION_PATTERN[$version - 7];
2578
2579            $v = $vinf;
2580            for ($x = 0; $x < 6; ++$x) {
2581                for ($y = 0; $y < 3; ++$y) {
2582                    $frame[($width - 11) + $y][$x] = \chr(0x88 | ($v & 1));
2583                    $v                           >>= 1;
2584                }
2585            }
2586
2587            $v = $vinf;
2588            for ($y = 0; $y < 6; ++$y) {
2589                for ($x = 0; $x < 3; ++$x) {
2590                    $frame[$y][$x + ($width - 11)] = \chr(0x88 | ($v & 1));
2591                    $v                           >>= 1;
2592                }
2593            }
2594        }
2595
2596        // and a little bit...
2597        $frame[$width - 8][8] = "\x81";
2598
2599        return $frame;
2600    }
2601
2602    /**
2603     * Set new frame for the specified version.
2604     *
2605     * @return array
2606     *
2607     * @since 1.0.0
2608     */
2609    protected function newFrame(int $version) : array
2610    {
2611        if ($version < 1 || $version > self::QRSPEC_VERSION_MAX) {
2612            return [];
2613        }
2614
2615        if (!isset($this->frames[$version])) {
2616            $this->frames[$version] = $this->createFrame($version);
2617        }
2618
2619        if ($this->frames[$version] === null) {
2620            return [];
2621        }
2622
2623        return $this->frames[$version];
2624    }
2625
2626    /**
2627     * Initialize a Reed-Solomon codec and add it to existing rsitems
2628     *
2629     * @return array
2630     *
2631     * @since 1.0.0
2632     */
2633    protected function init_rs(int $symsize, int $gfpoly, int $fcr, int $prim, int $nroots, int $pad) : array
2634    {
2635        foreach ($this->rsitems as $rs) {
2636            if (($rs['pad'] !== $pad) || ($rs['nroots'] !== $nroots) || ($rs['mm'] !== $symsize)
2637                || ($rs['gfpoly'] !== $gfpoly) || ($rs['fcr'] !== $fcr) || ($rs['prim'] !== $prim)
2638            ) {
2639                continue;
2640            }
2641
2642            return $rs;
2643        }
2644
2645        $rs = $this->init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad);
2646        \array_unshift($this->rsitems, $rs);
2647
2648        return $rs;
2649    }
2650
2651    /**
2652     * modnn
2653     *
2654     * @return int
2655     *
2656     * @since 1.0.0
2657     */
2658    protected function modnn(array $rs, int $x) : int
2659    {
2660        while ($x >= $rs['nn']) {
2661            $x -= $rs['nn'];
2662            $x  = ($x >> $rs['mm']) + ($x & $rs['nn']);
2663        }
2664
2665        return $x;
2666    }
2667
2668    /**
2669     * Initialize a Reed-Solomon codec and returns an array of values.
2670     *
2671     * @return array
2672     *
2673     * @since 1.0.0
2674     */
2675    protected function init_rs_char(int $symsize, int $gfpoly, int $fcr, int $prim, int $nroots, int $pad) : array
2676    {
2677        // Based on Reed solomon encoder by Phil Karn, KA9Q (GNU-LGPLv2)
2678        // Check parameter ranges
2679        if ($symsize < 0 || $symsize > 8
2680            || $fcr < 0 || $fcr >= (1 << $symsize)
2681            || $prim <= 0 || $prim >= (1 << $symsize)
2682            || $nroots < 0 || $nroots >= (1 << $symsize)
2683            || $pad < 0 || $pad >= ((1 << $symsize) - 1 - $nroots)
2684        ) {
2685            return [];
2686        }
2687
2688        $rs             = [];
2689        $rs['mm']       = $symsize;
2690        $rs['nn']       = (1 << $symsize) - 1;
2691        $rs['pad']      = $pad;
2692        $rs['alpha_to'] = \array_fill(0, ($rs['nn'] + 1), 0);
2693        $rs['index_of'] = \array_fill(0, ($rs['nn'] + 1), 0);
2694
2695        // PHP style macro replacement ;)
2696        $NN =& $rs['nn'];
2697        $A0 =& $NN;
2698
2699        // Generate Galois field lookup tables
2700        $rs['index_of'][0]   = $A0; // log(zero) = -inf
2701        $rs['alpha_to'][$A0] = 0; // alpha**-inf = 0
2702        $sr                  = 1;
2703
2704        for ($i = 0; $i < $rs['nn']; ++$i) {
2705            $rs['index_of'][$sr] = $i;
2706            $rs['alpha_to'][$i]  = $sr;
2707            $sr                <<= 1;
2708
2709            if (($sr & (1 << $symsize)) !== 0) {
2710                $sr ^= $gfpoly;
2711            }
2712
2713            $sr &= $rs['nn'];
2714        }
2715
2716        if ($sr !== 1) {
2717            // field generator polynomial is not primitive!
2718            return [];
2719        }
2720
2721        // Form RS code generator polynomial from its roots
2722        $rs['genpoly'] = \array_fill(0, ($nroots + 1), 0);
2723        $rs['fcr']     = $fcr;
2724        $rs['prim']    = $prim;
2725        $rs['nroots']  = $nroots;
2726        $rs['gfpoly']  = $gfpoly;
2727
2728        // Find prim-th root of 1, used in decoding
2729        $iprim = 1;
2730        while ($iprim % $prim !== 0) {
2731            $iprim += $rs['nn'];
2732        }
2733
2734        $rs['iprim']      = (int) ($iprim / $prim);
2735        $rs['genpoly'][0] = 1;
2736
2737        for ($i = 0, $root = $fcr * $prim; $i < $nroots; ++$i, $root += $prim) {
2738            $rs['genpoly'][$i + 1] = 1;
2739
2740            // Multiply rs->genpoly[] by  @**(root + x)
2741            for ($j = $i; $j > 0; --$j) {
2742                if ($rs['genpoly'][$j] !== 0) {
2743                    $rs['genpoly'][$j] = $rs['genpoly'][$j - 1] ^ $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][$j]] + $root)];
2744                } else {
2745                    $rs['genpoly'][$j] = $rs['genpoly'][$j - 1];
2746                }
2747            }
2748
2749            // rs->genpoly[0] can never be zero
2750            $rs['genpoly'][0] = $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][0]] + $root)];
2751        }
2752
2753        // convert rs->genpoly[] to index form for quicker encoding
2754        for ($i = 0; $i <= $nroots; ++$i) {
2755            $rs['genpoly'][$i] = $rs['index_of'][$rs['genpoly'][$i]];
2756        }
2757
2758        return $rs;
2759    }
2760
2761    /**
2762     * Encode a Reed-Solomon codec and returns the parity array
2763     *
2764     * @return array
2765     *
2766     * @since 1.0.0
2767     */
2768    protected function encode_rs_char(array $rs, array $data, array $parity) : array
2769    {
2770        $MM       =& $rs['mm']; // bits per symbol
2771        $NN       =& $rs['nn']; // the total number of symbols in a RS block
2772        $ALPHA_TO =& $rs['alpha_to']; // the address of an array of NN elements to convert Galois field elements in index (log) form to polynomial form
2773        $INDEX_OF =& $rs['index_of']; // the address of an array of NN elements to convert Galois field elements in polynomial form to index (log) form
2774        $GENPOLY  =& $rs['genpoly']; // an array of NROOTS+1 elements containing the generator polynomial in index form
2775        $NROOTS   =& $rs['nroots']; // the number of roots in the RS code generator polynomial, which is the same as the number of parity symbols in a block
2776        $FCR      =& $rs['fcr']; // first consecutive root, index form
2777        $PRIM     =& $rs['prim']; // primitive element, index form
2778        $IPRIM    =& $rs['iprim']; // prim-th root of 1, index form
2779        $PAD      =& $rs['pad']; // the number of pad symbols in a block
2780        $A0       =& $NN;
2781        $parity   = \array_fill(0, $NROOTS, 0);
2782
2783        for ($i = 0; $i < $NN - $NROOTS - $PAD; ++$i) {
2784            $feedback = $INDEX_OF[$data[$i] ^ $parity[0]];
2785
2786            if ($feedback !== $A0) {
2787                // feedback term is non-zero
2788                // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
2789                // always be for the polynomials constructed by init_rs()
2790                $feedback = $this->modnn($rs, $NN - $GENPOLY[$NROOTS] + $feedback);
2791
2792                for ($j = 1; $j < $NROOTS; ++$j) {
2793                    $parity[$j] ^= $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[($NROOTS - $j)])];
2794                }
2795            }
2796
2797            // Shift
2798            \array_shift($parity);
2799            $parity[] = $feedback !== $A0
2800                ? $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[0])]
2801                : 0;
2802        }
2803
2804        return $parity;
2805    }
2806}