qr_reader 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +16 -0
  3. data/LICENSE.txt +21 -0
  4. data/NOTICE +14 -0
  5. data/README.md +222 -0
  6. data/exe/qr_reader +66 -0
  7. data/lib/qr_reader/binarizer.rb +170 -0
  8. data/lib/qr_reader/bit_matrix.rb +120 -0
  9. data/lib/qr_reader/decoder/bitstream_parser.rb +307 -0
  10. data/lib/qr_reader/decoder/character_set.rb +163 -0
  11. data/lib/qr_reader/decoder/codewords.rb +164 -0
  12. data/lib/qr_reader/decoder/data_mask.rb +57 -0
  13. data/lib/qr_reader/decoder/format_reader.rb +204 -0
  14. data/lib/qr_reader/decoder/function_patterns.rb +62 -0
  15. data/lib/qr_reader/decoder/galois_field.rb +69 -0
  16. data/lib/qr_reader/decoder/reed_solomon.rb +312 -0
  17. data/lib/qr_reader/decoder.rb +241 -0
  18. data/lib/qr_reader/detector/alignment_grid.rb +127 -0
  19. data/lib/qr_reader/detector/alignment_pattern_finder.rb +191 -0
  20. data/lib/qr_reader/detector/candidate/dimension.rb +237 -0
  21. data/lib/qr_reader/detector/candidate/strokes.rb +126 -0
  22. data/lib/qr_reader/detector/candidate.rb +436 -0
  23. data/lib/qr_reader/detector/finder_pattern.rb +63 -0
  24. data/lib/qr_reader/detector/finder_pattern_finder.rb +199 -0
  25. data/lib/qr_reader/detector/geometry.rb +118 -0
  26. data/lib/qr_reader/detector/grid_sampler.rb +350 -0
  27. data/lib/qr_reader/detector/pattern_sets.rb +235 -0
  28. data/lib/qr_reader/detector/perspective_transform.rb +117 -0
  29. data/lib/qr_reader/detector/pure_detector.rb +197 -0
  30. data/lib/qr_reader/detector.rb +215 -0
  31. data/lib/qr_reader/errors.rb +14 -0
  32. data/lib/qr_reader/format_information.rb +79 -0
  33. data/lib/qr_reader/formats/bmp.rb +434 -0
  34. data/lib/qr_reader/formats/ccitt.rb +81 -0
  35. data/lib/qr_reader/formats/gif.rb +272 -0
  36. data/lib/qr_reader/formats/jpeg.rb +152 -0
  37. data/lib/qr_reader/formats/pdf.rb +2935 -0
  38. data/lib/qr_reader/formats/png.rb +311 -0
  39. data/lib/qr_reader/formats/pnm.rb +286 -0
  40. data/lib/qr_reader/formats/tiff.rb +1345 -0
  41. data/lib/qr_reader/formats.rb +279 -0
  42. data/lib/qr_reader/image.rb +231 -0
  43. data/lib/qr_reader/kernels/binarizer.rb +475 -0
  44. data/lib/qr_reader/kernels/ccitt.rb +554 -0
  45. data/lib/qr_reader/kernels/detector.rb +587 -0
  46. data/lib/qr_reader/kernels/image.rb +65 -0
  47. data/lib/qr_reader/kernels/jpeg.rb +3421 -0
  48. data/lib/qr_reader/kernels/lzw.rb +173 -0
  49. data/lib/qr_reader/kernels/pixels.rb +116 -0
  50. data/lib/qr_reader/kernels/png.rb +1138 -0
  51. data/lib/qr_reader/kernels.rb +14 -0
  52. data/lib/qr_reader/native_kernels.rb +130 -0
  53. data/lib/qr_reader/reader.rb +348 -0
  54. data/lib/qr_reader/result.rb +49 -0
  55. data/lib/qr_reader/symbol_version.rb +173 -0
  56. data/lib/qr_reader/version.rb +5 -0
  57. data/lib/qr_reader.rb +77 -0
  58. metadata +103 -0
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QrReader
4
+ module Decoder
5
+ # Reads the format information (EC level + mask) and version information
6
+ # words from a module grid. Bit order is exactly that of ISO/IEC 18004
7
+ # §7.9/§7.10 as implemented by ZXing's BitMatrixParser: the first module
8
+ # read becomes the most significant bit.
9
+ #
10
+ # +bits+ is the symbol's module String (0/1 bytes, row-major), +n+ its
11
+ # dimension. With +mirrored+ the symbol is read transposed.
12
+ module FormatReader
13
+ # Copy 1 around the top-left finder, as [x, y].
14
+ FORMAT_COPY1 = [[0, 8], [1, 8], [2, 8], [3, 8], [4, 8], [5, 8], [7, 8], [8, 8],
15
+ [8, 7], [8, 5], [8, 4], [8, 3], [8, 2], [8, 1], [8, 0]].freeze
16
+
17
+ # Version information code words for versions 7..40.
18
+ VERSION_CODES = (7..40).map { |v| SymbolVersion[v].version_info_bits }.freeze
19
+ POPCOUNT9 = Array.new(512) { |i| i.to_s(2).count("1") }.freeze
20
+ NO_CANDIDATES = [].freeze
21
+ private_constant :POPCOUNT9, :NO_CANDIDATES
22
+
23
+ @format_table = nil
24
+ @positions = {}
25
+
26
+ class << self
27
+ # The two 15-bit format information words [copy1, copy2]. Copy 2 runs
28
+ # up column 8 from the bottom-left corner (7 modules, above them is the
29
+ # dark module), then along row 8 to the right edge (8 modules).
30
+ def format_bits(bits, n, mirrored: false)
31
+ copy1, copy2 = positions(n, mirrored)
32
+ [read_word(bits, copy1), read_word(bits, copy2)]
33
+ end
34
+
35
+ # The two 18-bit version information words [top_right, bottom_left].
36
+ def version_bits(bits, n, mirrored: false)
37
+ top_right, bottom_left = positions(n, mirrored)[2]
38
+ [read_word(bits, top_right), read_word(bits, bottom_left)]
39
+ end
40
+
41
+ # Decodes the two format words like FormatInformation.decode (nearest
42
+ # code word within Hamming distance 3 of either copy, ties to the lower
43
+ # code; then the same for copies lacking the 0x5412 mask), but with one
44
+ # Array lookup per word. Returns [FormatInformation, distance] or nil.
45
+ def decode_format(copy1, copy2)
46
+ table = format_table
47
+ mask = FormatInformation::MASK
48
+ entry = closer(table[copy1 & 0x7FFF], table[copy2 & 0x7FFF]) ||
49
+ closer(table[(copy1 ^ mask) & 0x7FFF], table[(copy2 ^ mask) & 0x7FFF])
50
+ entry && [FormatInformation::ALL[entry >> 2], entry & 3]
51
+ end
52
+
53
+ # Every format information worth trying for the two copies, most
54
+ # likely first: Array of up to two [FormatInformation, distance].
55
+ #
56
+ # Each copy proposes the code word within Hamming distance 3 of it
57
+ # (as in #decode_format, masked code words first, then copies lacking
58
+ # the 0x5412 mask). When the copies propose different code words, the
59
+ # one needing fewer bit errors in both copies together goes first
60
+ # (ties: the smaller distance of a single copy, then the lower code):
61
+ # ranking by one copy alone picks the wrong one when both copies are
62
+ # damaged, and trying the runner-up rescues the symbol when it is.
63
+ # +distance+ is the smaller distance of the two copies.
64
+ def format_candidates(copy1, copy2)
65
+ table = format_table
66
+ copy1 &= 0x7FFF
67
+ copy2 &= 0x7FFF
68
+ a = table[copy1]
69
+ b = table[copy2]
70
+ unless a || b
71
+ mask = FormatInformation::MASK
72
+ copy1 ^= mask
73
+ copy2 ^= mask
74
+ a = table[copy1]
75
+ b = table[copy2]
76
+ return NO_CANDIDATES unless a || b
77
+ end
78
+ return [candidate(a, copy2)] unless b
79
+ return [candidate(b, copy1)] if a.nil? || a >> 2 == b >> 2
80
+
81
+ first = candidate(a, copy2)
82
+ second = candidate(b, copy1)
83
+ first_rank = [(a & 3) + popcount15(copy2 ^ first[0].bits), a & 3, a >> 2]
84
+ second_rank = [(b & 3) + popcount15(copy1 ^ second[0].bits), b & 3, b >> 2]
85
+ (second_rank <=> first_rank).negative? ? [second, first] : [first, second]
86
+ end
87
+
88
+ # Version number (7..40) whose code word is nearest to the 18-bit
89
+ # +word+ within Hamming distance 3 (ties to the lower version), or nil.
90
+ # Same result as SymbolVersion.decode_version_info, without allocation.
91
+ def decode_version(word)
92
+ best = nil
93
+ best_distance = 4
94
+ i = 0
95
+ while i < 34
96
+ x = VERSION_CODES[i] ^ word
97
+ return i + 7 if x.zero?
98
+
99
+ distance = POPCOUNT9[x & 511] + POPCOUNT9[(x >> 9) & 511]
100
+ if distance < best_distance
101
+ best = i + 7
102
+ best_distance = distance
103
+ end
104
+ i += 1
105
+ end
106
+ best
107
+ end
108
+
109
+ private
110
+
111
+ # Word made of the modules at +indices+, first index = most significant bit.
112
+ def read_word(bits, indices)
113
+ word = 0
114
+ i = 0
115
+ count = indices.size
116
+ while i < count
117
+ word = (word << 1) | bits.getbyte(indices[i])
118
+ i += 1
119
+ end
120
+ word
121
+ end
122
+
123
+ # Module String indices, in reading order, of format copy 1, format
124
+ # copy 2 and the two version information blocks for dimension +n+,
125
+ # cached per dimension and orientation.
126
+ def positions(n, mirrored)
127
+ @positions[mirrored ? -n : n] ||= begin
128
+ index = mirrored ? ->(x, y) { x * n + y } : ->(x, y) { y * n + x }
129
+ copy2 = (n - 1).downto(n - 7).map { |y| [8, y] } + (n - 8).upto(n - 1).map { |x| [x, 8] }
130
+ top_right = 5.downto(0).flat_map { |y| (n - 9).downto(n - 11).map { |x| [x, y] } }
131
+ bottom_left = 5.downto(0).flat_map { |x| (n - 9).downto(n - 11).map { |y| [x, y] } }
132
+ words = [FORMAT_COPY1, copy2, top_right, bottom_left].map do |coords|
133
+ coords.map { |x, y| index.call(x, y) }.freeze
134
+ end
135
+ [words[0], words[1], words[2, 2].freeze].freeze
136
+ end
137
+ end
138
+
139
+ # [FormatInformation, distance] for a format table +entry+ proposed by
140
+ # one copy; +other+ is the other copy (it may be closer).
141
+ def candidate(entry, other)
142
+ info = FormatInformation::ALL[entry >> 2]
143
+ distance = entry & 3
144
+ other_distance = popcount15(other ^ info.bits)
145
+ [info, other_distance < distance ? other_distance : distance]
146
+ end
147
+
148
+ def popcount15(word)
149
+ POPCOUNT9[word & 511] + POPCOUNT9[word >> 9]
150
+ end
151
+
152
+ def closer(a, b)
153
+ return b unless a
154
+ return a unless b
155
+
156
+ da = a & 3
157
+ db = b & 3
158
+ return a if da < db
159
+ return b if db < da
160
+
161
+ a < b ? a : b
162
+ end
163
+
164
+ def format_table
165
+ @format_table ||= build_format_table
166
+ end
167
+
168
+ # 32768 entries: (format data bits << 2 | distance) for every 15-bit
169
+ # word within distance 3 of a format code word, else nil. The code has
170
+ # minimum distance 7, so these neighbourhoods never overlap. (Plain
171
+ # loops in a method that runs once: YJIT would spend more time
172
+ # compiling blocks here than running them.)
173
+ def build_format_table
174
+ table = Array.new(1 << 15)
175
+ data = 0
176
+ while data < 32
177
+ code = FormatInformation::ALL[data].bits
178
+ base = data << 2
179
+ table[code] = base
180
+ a = 0
181
+ while a < 15
182
+ one = code ^ (1 << a)
183
+ table[one] = base | 1
184
+ b = 0
185
+ while b < a
186
+ two = one ^ (1 << b)
187
+ table[two] = base | 2
188
+ c = 0
189
+ while c < b
190
+ table[two ^ (1 << c)] = base | 3
191
+ c += 1
192
+ end
193
+ b += 1
194
+ end
195
+ a += 1
196
+ end
197
+ data += 1
198
+ end
199
+ table.freeze
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QrReader
4
+ module Decoder
5
+ # Which modules of a symbol are function modules (finder patterns with
6
+ # separators, format and version information, timing patterns, alignment
7
+ # patterns, dark module) and therefore carry no data. Same regions as
8
+ # ZXing's Version#buildFunctionPattern.
9
+ module FunctionPatterns
10
+ @cache = Array.new(41)
11
+
12
+ class << self
13
+ # Binary String of dimension**2 bytes, row-major, "\x01" for function
14
+ # modules. Cached per version (1..40).
15
+ def for_version(number)
16
+ @cache[number] ||= build(SymbolVersion[number])
17
+ end
18
+
19
+ private
20
+
21
+ # (Plain loops: this runs once per version, and YJIT would spend more
22
+ # time compiling blocks here than running them.)
23
+ def build(version)
24
+ n = version.dimension
25
+ grid = "\0".b * (n * n)
26
+ fill(grid, n, 0, 0, 9, 9) # top-left finder + separator + format info
27
+ fill(grid, n, n - 8, 0, 8, 9) # top-right finder + separator + format info
28
+ fill(grid, n, 0, n - 8, 9, 8) # bottom-left finder + separator + format info + dark module
29
+ centers = version.alignment_centers
30
+ last = centers.size - 1
31
+ iy = 0
32
+ while iy <= last
33
+ ix = 0
34
+ while ix <= last
35
+ # the three corners occupied by finder patterns carry none
36
+ finder = (ix.zero? && (iy.zero? || iy == last)) || (ix == last && iy.zero?)
37
+ fill(grid, n, centers[ix] - 2, centers[iy] - 2, 5, 5) unless finder
38
+ ix += 1
39
+ end
40
+ iy += 1
41
+ end
42
+ fill(grid, n, 6, 9, 1, n - 17) # vertical timing pattern
43
+ fill(grid, n, 9, 6, n - 17, 1) # horizontal timing pattern
44
+ if version.number >= 7
45
+ fill(grid, n, n - 11, 0, 3, 6) # version info, top right
46
+ fill(grid, n, 0, n - 11, 6, 3) # version info, bottom left
47
+ end
48
+ grid.freeze
49
+ end
50
+
51
+ def fill(grid, n, left, top, width, height)
52
+ row = "\x01".b * width
53
+ y = top
54
+ while y < top + height
55
+ grid[y * n + left, width] = row
56
+ y += 1
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QrReader
4
+ module Decoder
5
+ # Arithmetic in GF(256) as used by QR codes: field polynomial
6
+ # x^8 + x^4 + x^3 + x^2 + 1 (0x11D), primitive element alpha = 2.
7
+ # Addition is XOR; multiplication goes through log/antilog tables.
8
+ module GaloisField
9
+ PRIMITIVE = 0x11D
10
+
11
+ exp = Array.new(512, 0)
12
+ log = Array.new(256, 0)
13
+ x = 1
14
+ 255.times do |i|
15
+ exp[i] = x
16
+ log[x] = i
17
+ x <<= 1
18
+ x ^= PRIMITIVE if x > 255
19
+ end
20
+ 255.upto(511) { |i| exp[i] = exp[i - 255] }
21
+
22
+ # EXP[i] = alpha**i for 0 <= i < 512. Doubled in length so that
23
+ # EXP[LOG[a] + LOG[b]] needs no modulo.
24
+ EXP = exp.freeze
25
+ # LOG[a] = discrete log of a (1..255). LOG[0] is undefined (stored as 0).
26
+ LOG = log.freeze
27
+
28
+ # MULTIPLY_BY_ALPHA_POWER[j][s] = s * alpha**j, for j < 31 (enough for the
29
+ # largest QR block's 30 error correction codewords). Lets a syndrome
30
+ # Horner step be one Array lookup: s = table[s] ^ codeword.
31
+ MULTIPLY_BY_ALPHA_POWER = Array.new(31) do |j|
32
+ Array.new(256) { |s| s.zero? ? 0 : exp[log[s] + j] }.freeze
33
+ end.freeze
34
+
35
+ @multiplication_table = nil
36
+
37
+ # Full multiplication table: multiplication_table[a][b] = a * b. Built on
38
+ # first use because only error correction needs it. Row a is EXP
39
+ # shifted by LOG[a] and permuted by LOG, gathered at C speed.
40
+ def self.multiplication_table
41
+ @multiplication_table ||= begin
42
+ logs = LOG[1, 255]
43
+ zero = Array.new(256, 0).freeze
44
+ Array.new(256) do |a|
45
+ next zero if a.zero?
46
+
47
+ EXP[LOG[a], 255].values_at(*logs).unshift(0).freeze
48
+ end.freeze
49
+ end
50
+ end
51
+
52
+ module_function
53
+
54
+ def multiply(a, b)
55
+ a.zero? || b.zero? ? 0 : EXP[LOG[a] + LOG[b]]
56
+ end
57
+
58
+ # a / b for b != 0.
59
+ def divide(a, b)
60
+ a.zero? ? 0 : EXP[LOG[a] + 255 - LOG[b]]
61
+ end
62
+
63
+ # alpha**power for any integer power (negative allowed).
64
+ def power(power)
65
+ EXP[power % 255]
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,312 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QrReader
4
+ module Decoder
5
+ # Reed-Solomon error correction for QR code blocks over GF(256) (0x11D).
6
+ #
7
+ # The generator polynomial of an EC block with +ec+ codewords has the roots
8
+ # alpha**0 .. alpha**(ec-1). Decoding computes the syndromes, finds the
9
+ # error locator with Berlekamp-Massey, its roots with a Chien search and the
10
+ # error values with Forney's formula, then re-checks the syndromes of the
11
+ # corrected block. At most floor(ec/2) errors are corrected; anything that
12
+ # does not lead to a verified codeword is rejected.
13
+ #
14
+ # Polynomials are Arrays of coefficients; a block's codewords[0] is the
15
+ # coefficient of x**(n-1), so codeword i sits at "degree position" n-1-i.
16
+ module ReedSolomon
17
+ EXP = GaloisField::EXP
18
+ LOG = GaloisField::LOG
19
+ MULTIPLY_BY_ALPHA_POWER = GaloisField::MULTIPLY_BY_ALPHA_POWER
20
+
21
+ module_function
22
+
23
+ # Corrects +codewords+ (Array of Integers 0..255, data followed by EC
24
+ # codewords, first element = highest-degree coefficient) in place.
25
+ # Returns the number of corrected codewords, or nil when the block is
26
+ # uncorrectable (the Array is then left unchanged).
27
+ def decode(codewords, ec_count)
28
+ n = codewords.size
29
+ return nil if ec_count < 1 || ec_count >= n || n > 255 || ec_count >= MULTIPLY_BY_ALPHA_POWER.size
30
+
31
+ syndromes = syndromes(codewords, ec_count)
32
+ return 0 unless syndromes
33
+
34
+ locator = berlekamp_massey(syndromes)
35
+ errors = locator.size - 1
36
+ return nil if errors.zero? || 2 * errors > ec_count
37
+
38
+ positions = chien_search(locator, n)
39
+ return nil unless positions
40
+
41
+ magnitudes = forney(syndromes, locator, positions)
42
+ return nil unless magnitudes && corrects_syndromes?(syndromes, positions, magnitudes)
43
+
44
+ positions.each_with_index { |e, k| codewords[n - 1 - e] ^= magnitudes[k] }
45
+ errors
46
+ end
47
+
48
+ # Syndromes S_j = r(alpha**j) for j = 0...ec_count, or nil when all are
49
+ # zero (the block is a valid codeword). This is the hot path for clean
50
+ # symbols, so the cheaper of two equivalent methods is used: with a JIT,
51
+ # Horner's rule over all codewords (allocation-free table lookups);
52
+ # in the plain interpreter, the remainder r(x) mod g(x) computed with
53
+ # one Bignum step per codeword, whose syndromes are those of r(x)
54
+ # because g(alpha**j) = 0 (about 3x faster there).
55
+ def syndromes(codewords, ec_count)
56
+ jit? ? horner_syndromes(codewords, ec_count) : remainder_syndromes(codewords, ec_count)
57
+ end
58
+
59
+ # Syndromes by Horner's rule: one lookup and one XOR per codeword and
60
+ # syndrome. Four syndromes share each pass over the codewords, which
61
+ # roughly halves the loop overhead.
62
+ def horner_syndromes(codewords, ec_count)
63
+ n = codewords.size
64
+ result = nil
65
+ j = 0
66
+ while j + 4 <= ec_count
67
+ t0 = MULTIPLY_BY_ALPHA_POWER[j]
68
+ t1 = MULTIPLY_BY_ALPHA_POWER[j + 1]
69
+ t2 = MULTIPLY_BY_ALPHA_POWER[j + 2]
70
+ t3 = MULTIPLY_BY_ALPHA_POWER[j + 3]
71
+ s0 = s1 = s2 = s3 = 0
72
+ i = 0
73
+ while i < n
74
+ c = codewords[i]
75
+ s0 = t0[s0] ^ c
76
+ s1 = t1[s1] ^ c
77
+ s2 = t2[s2] ^ c
78
+ s3 = t3[s3] ^ c
79
+ i += 1
80
+ end
81
+ if (s0 | s1 | s2 | s3) != 0
82
+ result ||= Array.new(ec_count, 0)
83
+ result[j] = s0
84
+ result[j + 1] = s1
85
+ result[j + 2] = s2
86
+ result[j + 3] = s3
87
+ end
88
+ j += 4
89
+ end
90
+ while j < ec_count
91
+ table = MULTIPLY_BY_ALPHA_POWER[j]
92
+ s = 0
93
+ i = 0
94
+ while i < n
95
+ s = table[s] ^ codewords[i]
96
+ i += 1
97
+ end
98
+ if s != 0
99
+ result ||= Array.new(ec_count, 0)
100
+ result[j] = s
101
+ end
102
+ j += 1
103
+ end
104
+ result
105
+ end
106
+
107
+ # Syndromes via the remainder r(x) mod g(x), kept as an Integer of
108
+ # ec_count bytes (coefficient of x**(ec-1) in the top byte).
109
+ def remainder_syndromes(codewords, ec_count)
110
+ table = remainder_table(ec_count)
111
+ shift = 8 * (ec_count - 1)
112
+ r = 0
113
+ i = 0
114
+ n = codewords.size
115
+ while i < n
116
+ r = ((r << 8) | codewords[i]) ^ table[r >> shift]
117
+ i += 1
118
+ end
119
+ return nil if r.zero?
120
+
121
+ horner_syndromes([r.to_s(16).rjust(2 * ec_count, "0")].pack("H*").bytes, ec_count)
122
+ end
123
+
124
+ # True when running under a JIT compiler (or a non-CRuby VM).
125
+ def jit?
126
+ return true unless RUBY_ENGINE == "ruby"
127
+
128
+ yjit = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
129
+ zjit = defined?(RubyVM::ZJIT) && RubyVM::ZJIT.enabled?
130
+ yjit || zjit ? true : false
131
+ end
132
+
133
+ @remainder_tables = []
134
+
135
+ # remainder_table(ec)[b] = b * g(x) as an Integer of ec + 1 bytes, where
136
+ # g(x) = (x - alpha**0) ... (x - alpha**(ec-1)) is the monic generator.
137
+ # The map b -> b * g(x) is GF(2)-linear, so only the eight single-bit
138
+ # rows need field multiplications; the others are XORs of those.
139
+ def remainder_table(ec_count)
140
+ @remainder_tables[ec_count] ||= begin
141
+ generator = [1]
142
+ ec_count.times do |i|
143
+ next_generator = generator + [0]
144
+ generator.each_with_index { |c, k| next_generator[k + 1] ^= GaloisField.multiply(c, EXP[i]) }
145
+ generator = next_generator
146
+ end
147
+ table = Array.new(256, 0)
148
+ 8.times do |bit|
149
+ b = 1 << bit
150
+ table[b] = generator.inject(0) { |acc, c| (acc << 8) | GaloisField.multiply(b, c) }
151
+ end
152
+ b = 1
153
+ while b < 256
154
+ low = b & -b # lowest set bit; table[b ^ low] is already filled
155
+ table[b] = table[low] ^ table[b ^ low] unless b == low
156
+ b += 1
157
+ end
158
+ table.freeze
159
+ end
160
+ end
161
+
162
+ # Error locator polynomial Lambda (coefficients, Lambda[0] = 1) of the
163
+ # shortest LFSR generating the syndrome sequence. Its length - 1 is the
164
+ # number of errors assumed (its degree).
165
+ def berlekamp_massey(syndromes)
166
+ mul = GaloisField.multiplication_table
167
+ current = [1]
168
+ previous = [1]
169
+ length = 0
170
+ shift = 1
171
+ previous_discrepancy = 1
172
+ count = syndromes.size
173
+ n = 0
174
+ while n < count
175
+ d = syndromes[n]
176
+ i = 1
177
+ while i <= length
178
+ d ^= mul[current[i]][syndromes[n - i]]
179
+ i += 1
180
+ end
181
+ if d.zero?
182
+ shift += 1
183
+ else
184
+ row = mul[EXP[LOG[d] + 255 - LOG[previous_discrepancy]]] # d / previous_discrepancy
185
+ replaced = 2 * length <= n ? current.dup : nil
186
+ size = previous.size
187
+ current << 0 while current.size < size + shift
188
+ k = 0
189
+ while k < size
190
+ current[k + shift] ^= row[previous[k]]
191
+ k += 1
192
+ end
193
+ if replaced
194
+ length = n + 1 - length
195
+ previous = replaced
196
+ previous_discrepancy = d
197
+ shift = 1
198
+ else
199
+ shift += 1
200
+ end
201
+ end
202
+ n += 1
203
+ end
204
+ current.pop while current.size > length + 1
205
+ current << 0 while current.size < length + 1 # degree below length: no valid locator
206
+ current
207
+ end
208
+
209
+ # Degree positions e (0...n) of the errors: the e with
210
+ # Lambda(alpha**-e) == 0, by Horner evaluation. Returns nil unless
211
+ # exactly deg(Lambda) roots lie inside the block.
212
+ def chien_search(locator, n)
213
+ mul = GaloisField.multiplication_table
214
+ errors = locator.size - 1
215
+ return nil if locator[errors].zero?
216
+
217
+ roots = []
218
+ e = 0
219
+ while e < n
220
+ row = mul[EXP[255 - e]] # x = alpha**-e
221
+ s = locator[errors]
222
+ k = errors - 1
223
+ while k >= 0
224
+ s = row[s] ^ locator[k]
225
+ k -= 1
226
+ end
227
+ if s.zero?
228
+ roots << e
229
+ return roots if roots.size == errors
230
+ end
231
+ e += 1
232
+ end
233
+ nil
234
+ end
235
+
236
+ # Error values by Forney's formula (first consecutive root alpha**0):
237
+ # e_k = X_k * Omega(X_k^-1) / Lambda'(X_k^-1), Omega = S * Lambda mod x^v.
238
+ # Returns nil if a value comes out as zero (no valid correction).
239
+ def forney(syndromes, locator, positions)
240
+ mul = GaloisField.multiplication_table
241
+ errors = locator.size - 1
242
+ omega = Array.new(errors, 0)
243
+ i = 0
244
+ while i < errors
245
+ v = 0
246
+ k = 0
247
+ while k <= i
248
+ v ^= mul[locator[k]][syndromes[i - k]]
249
+ k += 1
250
+ end
251
+ omega[i] = v
252
+ i += 1
253
+ end
254
+
255
+ count = positions.size
256
+ magnitudes = Array.new(count)
257
+ p = 0
258
+ while p < count
259
+ e = positions[p]
260
+ x_inverse = EXP[255 - e]
261
+ row = mul[x_inverse]
262
+ numerator = omega[errors - 1]
263
+ i = errors - 2
264
+ while i >= 0
265
+ numerator = row[numerator] ^ omega[i]
266
+ i -= 1
267
+ end
268
+ # Lambda'(x) has only the odd-degree terms (characteristic 2).
269
+ x_squared = row[x_inverse]
270
+ denominator = 0
271
+ power = 1
272
+ k = 1
273
+ while k <= errors
274
+ denominator ^= mul[locator[k]][power]
275
+ power = mul[power][x_squared]
276
+ k += 2
277
+ end
278
+ return nil if numerator.zero? || denominator.zero?
279
+
280
+ magnitudes[p] = EXP[(e + LOG[numerator] + 255 - LOG[denominator]) % 255]
281
+ p += 1
282
+ end
283
+ magnitudes
284
+ end
285
+
286
+ # True if subtracting the error values at the positions zeroes every
287
+ # syndrome, i.e. the corrected block is a codeword.
288
+ def corrects_syndromes?(syndromes, positions, magnitudes)
289
+ mul = GaloisField.multiplication_table
290
+ count = positions.size
291
+ locators = Array.new(count) { |k| EXP[positions[k]] }
292
+ terms = magnitudes.dup # magnitude_k * X_k**j, starting at j = 0
293
+ j = 0
294
+ ec_count = syndromes.size
295
+ while j < ec_count
296
+ s = syndromes[j]
297
+ k = 0
298
+ while k < count
299
+ term = terms[k]
300
+ s ^= term
301
+ terms[k] = mul[term][locators[k]]
302
+ k += 1
303
+ end
304
+ return false unless s.zero?
305
+
306
+ j += 1
307
+ end
308
+ true
309
+ end
310
+ end
311
+ end
312
+ end