@sythos/js_barcode_universal 1.5.8 → 1.5.9

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 (179) hide show
  1. package/LICENSE +5 -4
  2. package/NOTICE.md +2 -2
  3. package/README.md +17 -13
  4. package/bundle/sythos-barcode.esm.js +11658 -11244
  5. package/bundle/sythos-barcode.js +11658 -11244
  6. package/package.json +10 -3
  7. package/src/index.d.ts +1 -1
  8. package/src/index.js +508 -516
  9. package/src/js/aztec/decoder.js +208 -199
  10. package/src/js/aztec/detector.js +153 -126
  11. package/src/js/aztec/encoder.js +172 -153
  12. package/src/js/aztec/high-level.js +106 -101
  13. package/src/js/aztec/index.js +1 -12
  14. package/src/js/aztec/tables.js +106 -120
  15. package/src/js/aztecrune/decoder.js +76 -85
  16. package/src/js/aztecrune/detector.js +107 -93
  17. package/src/js/aztecrune/encoder.js +50 -68
  18. package/src/js/aztecrune/index.js +1 -16
  19. package/src/js/aztecrune/tables.js +59 -54
  20. package/src/js/compactpdf417/decoder.js +69 -74
  21. package/src/js/compactpdf417/detector.js +74 -75
  22. package/src/js/compactpdf417/encoder.js +68 -81
  23. package/src/js/compactpdf417/index.js +1 -13
  24. package/src/js/compactpdf417/tables.js +71 -80
  25. package/src/js/core/bit-buffer.js +106 -122
  26. package/src/js/core/bit-matrix.js +196 -194
  27. package/src/js/core/errors.js +13 -15
  28. package/src/js/core/galois-field.js +119 -133
  29. package/src/js/core/index.js +2 -19
  30. package/src/js/core/reed-solomon.js +195 -204
  31. package/src/js/databar/codec.js +93 -108
  32. package/src/js/databar/decoder.js +149 -140
  33. package/src/js/databar/encoder.js +47 -55
  34. package/src/js/databar/gs1.js +122 -123
  35. package/src/js/databar/index.js +4 -36
  36. package/src/js/databar/patterns.js +81 -85
  37. package/src/js/databar/tables.js +51 -52
  38. package/src/js/datamatrix/decoder.js +216 -182
  39. package/src/js/datamatrix/detector.js +161 -132
  40. package/src/js/datamatrix/encoder.js +192 -133
  41. package/src/js/datamatrix/index.js +1 -9
  42. package/src/js/datamatrix/tables.js +68 -69
  43. package/src/js/frameqr/decoder.js +102 -125
  44. package/src/js/frameqr/detector.js +101 -94
  45. package/src/js/frameqr/encoder.js +67 -80
  46. package/src/js/frameqr/index.js +1 -10
  47. package/src/js/frameqr/tables.js +145 -163
  48. package/src/js/image/binarizer.js +146 -162
  49. package/src/js/image/grid-sampler.js +63 -79
  50. package/src/js/image/index.js +0 -2
  51. package/src/js/image/luminance.js +129 -140
  52. package/src/js/image/perspective.js +112 -149
  53. package/src/js/micropdf417/compaction.js +64 -65
  54. package/src/js/micropdf417/decoder.js +106 -109
  55. package/src/js/micropdf417/detector.js +73 -70
  56. package/src/js/micropdf417/encoder.js +129 -154
  57. package/src/js/micropdf417/error-correction.js +6 -11
  58. package/src/js/micropdf417/index.js +2 -16
  59. package/src/js/micropdf417/tables.js +112 -99
  60. package/src/js/microqr/decoder.js +177 -182
  61. package/src/js/microqr/detector.js +240 -256
  62. package/src/js/microqr/encoder.js +239 -194
  63. package/src/js/microqr/index.js +0 -2
  64. package/src/js/microqr/tables.js +208 -200
  65. package/src/js/oned/addons.js +167 -191
  66. package/src/js/oned/index.js +27 -58
  67. package/src/js/oned/patterns.js +194 -213
  68. package/src/js/oned/reader.js +1003 -984
  69. package/src/js/oned/writers.js +411 -466
  70. package/src/js/pdf417/compaction.js +315 -222
  71. package/src/js/pdf417/decoder.js +71 -36
  72. package/src/js/pdf417/detector.js +377 -361
  73. package/src/js/pdf417/encoder.js +57 -46
  74. package/src/js/pdf417/error-correction.js +5 -8
  75. package/src/js/pdf417/tables.js +248 -250
  76. package/src/js/qr/decoder.js +311 -347
  77. package/src/js/qr/detector.js +378 -395
  78. package/src/js/qr/encoder.js +514 -599
  79. package/src/js/qr/index.js +0 -2
  80. package/src/js/qr/tables.js +375 -422
  81. package/src/js/render/image-data.js +57 -62
  82. package/src/js/render/index.js +21 -23
  83. package/src/js/render/options.js +82 -90
  84. package/src/js/render/png.js +144 -169
  85. package/src/js/render/svg.js +41 -44
  86. package/src/js/render/webgl.js +103 -109
  87. package/src/js/render/webgpu.js +205 -217
  88. package/src/js/rmqr/decoder.js +161 -53
  89. package/src/js/rmqr/detector.js +73 -41
  90. package/src/js/rmqr/encoder.js +253 -104
  91. package/src/js/rmqr/index.js +1 -5
  92. package/src/js/rmqr/tables.js +108 -85
  93. package/src/ts/aztec/decoder.ts +317 -0
  94. package/src/ts/aztec/detector.ts +224 -0
  95. package/src/ts/aztec/encoder.ts +257 -0
  96. package/src/ts/aztec/high-level.ts +211 -0
  97. package/src/ts/aztec/index.ts +45 -0
  98. package/src/ts/aztec/tables.ts +210 -0
  99. package/src/ts/aztecrune/decoder.ts +155 -0
  100. package/src/ts/aztecrune/detector.ts +166 -0
  101. package/src/ts/aztecrune/encoder.ts +121 -0
  102. package/src/ts/aztecrune/index.ts +49 -0
  103. package/src/ts/aztecrune/tables.ts +137 -0
  104. package/src/ts/compactpdf417/decoder.ts +128 -0
  105. package/src/ts/compactpdf417/detector.ts +139 -0
  106. package/src/ts/compactpdf417/encoder.ts +140 -0
  107. package/src/ts/compactpdf417/index.ts +17 -0
  108. package/src/ts/compactpdf417/tables.ts +173 -0
  109. package/src/ts/core/bit-buffer.ts +174 -0
  110. package/src/ts/core/bit-matrix.ts +241 -0
  111. package/src/ts/core/errors.ts +61 -0
  112. package/src/ts/core/galois-field.ts +207 -0
  113. package/src/ts/core/index.ts +56 -0
  114. package/src/ts/core/reed-solomon.ts +327 -0
  115. package/src/ts/databar/codec.ts +181 -0
  116. package/src/ts/databar/decoder.ts +215 -0
  117. package/src/ts/databar/encoder.ts +96 -0
  118. package/src/ts/databar/gs1.ts +177 -0
  119. package/src/ts/databar/index.ts +68 -0
  120. package/src/ts/databar/patterns.ts +143 -0
  121. package/src/ts/databar/tables.ts +124 -0
  122. package/src/ts/datamatrix/decoder.ts +262 -0
  123. package/src/ts/datamatrix/detector.ts +225 -0
  124. package/src/ts/datamatrix/encoder.ts +191 -0
  125. package/src/ts/datamatrix/index.ts +42 -0
  126. package/src/ts/datamatrix/tables.ts +123 -0
  127. package/src/ts/frameqr/decoder.ts +239 -0
  128. package/src/ts/frameqr/detector.ts +192 -0
  129. package/src/ts/frameqr/encoder.ts +156 -0
  130. package/src/ts/frameqr/index.ts +42 -0
  131. package/src/ts/frameqr/tables.ts +270 -0
  132. package/src/ts/image/binarizer.ts +270 -0
  133. package/src/ts/image/grid-sampler.ts +164 -0
  134. package/src/ts/image/index.ts +40 -0
  135. package/src/ts/image/luminance.ts +196 -0
  136. package/src/ts/image/perspective.ts +195 -0
  137. package/src/ts/index.d.ts +1 -1
  138. package/src/ts/index.ts +790 -0
  139. package/src/ts/micropdf417/compaction.ts +116 -0
  140. package/src/ts/micropdf417/decoder.ts +183 -0
  141. package/src/ts/micropdf417/detector.ts +149 -0
  142. package/src/ts/micropdf417/encoder.ts +209 -0
  143. package/src/ts/micropdf417/error-correction.ts +55 -0
  144. package/src/ts/micropdf417/index.ts +49 -0
  145. package/src/ts/micropdf417/tables.ts +184 -0
  146. package/src/ts/microqr/decoder.ts +245 -0
  147. package/src/ts/microqr/detector.ts +355 -0
  148. package/src/ts/microqr/encoder.ts +269 -0
  149. package/src/ts/microqr/index.ts +36 -0
  150. package/src/ts/microqr/tables.ts +316 -0
  151. package/src/ts/oned/addons.ts +420 -0
  152. package/src/ts/oned/index.ts +106 -0
  153. package/src/ts/oned/patterns.ts +384 -0
  154. package/src/ts/oned/reader.ts +1364 -0
  155. package/src/ts/oned/writers.ts +746 -0
  156. package/src/ts/pdf417/compaction.ts +298 -0
  157. package/src/ts/pdf417/decoder.ts +75 -0
  158. package/src/ts/pdf417/detector.ts +468 -0
  159. package/src/ts/pdf417/encoder.ts +91 -0
  160. package/src/ts/pdf417/error-correction.ts +47 -0
  161. package/src/ts/pdf417/index.ts +6 -0
  162. package/src/ts/pdf417/tables.ts +317 -0
  163. package/src/ts/qr/decoder.ts +575 -0
  164. package/src/ts/qr/detector.ts +630 -0
  165. package/src/ts/qr/encoder.ts +958 -0
  166. package/src/ts/qr/index.ts +44 -0
  167. package/src/ts/qr/tables.ts +737 -0
  168. package/src/ts/render/image-data.ts +125 -0
  169. package/src/ts/render/index.ts +130 -0
  170. package/src/ts/render/options.ts +160 -0
  171. package/src/ts/render/png.ts +295 -0
  172. package/src/ts/render/svg.ts +120 -0
  173. package/src/ts/render/webgl.ts +206 -0
  174. package/src/ts/render/webgpu.ts +369 -0
  175. package/src/ts/rmqr/decoder.ts +101 -0
  176. package/src/ts/rmqr/detector.ts +90 -0
  177. package/src/ts/rmqr/encoder.ts +172 -0
  178. package/src/ts/rmqr/index.ts +37 -0
  179. package/src/ts/rmqr/tables.ts +154 -0
@@ -27,7 +27,6 @@
27
27
  *
28
28
  * Original work. No code from any other barcode implementation.
29
29
  */
30
-
31
30
  /**
32
31
  * Reed-Solomon encoding and decoding over an arbitrary finite field.
33
32
  *
@@ -48,9 +47,7 @@
48
47
  *
49
48
  * @module core/reed-solomon
50
49
  */
51
-
52
50
  import { ChecksumError } from './errors.js';
53
-
54
51
  /**
55
52
  * Build the generator polynomial for `eccLen` parity symbols.
56
53
  *
@@ -64,22 +61,20 @@ import { ChecksumError } from './errors.js';
64
61
  * @returns {number[]} Monic, degree-descending, length eccLen + 1.
65
62
  */
66
63
  export function generatorPoly(eccLen, field, base = 0) {
67
- let g = [1];
68
- for (let i = 0; i < eccLen; i++) {
69
- const root = field.exp(base + i);
70
- const next = new Array(g.length + 1).fill(0);
71
- for (let j = 0; j < g.length; j++) {
72
- // g[j]*x lands at next[j]; g[j]*(-root) lands at next[j+1].
73
- next[j] = field.add(next[j], g[j]);
74
- next[j + 1] = field.sub(next[j + 1], field.mul(g[j], root));
64
+ let g = [1];
65
+ for (let i = 0; i < eccLen; i++) {
66
+ const root = field.exp(base + i);
67
+ const next = new Array(g.length + 1).fill(0);
68
+ for (let j = 0; j < g.length; j++) {
69
+ // g[j]*x lands at next[j]; g[j]*(-root) lands at next[j+1].
70
+ next[j] = field.add(next[j], g[j]);
71
+ next[j + 1] = field.sub(next[j + 1], field.mul(g[j], root));
72
+ }
73
+ g = next;
75
74
  }
76
- g = next;
77
- }
78
- return g;
75
+ return g;
79
76
  }
80
-
81
77
  const generatorCache = new Map();
82
-
83
78
  /**
84
79
  * Cached {@link generatorPoly}. Encoding the same format repeatedly is the
85
80
  * common case and rebuilding the polynomial each time is pure waste.
@@ -90,15 +85,14 @@ const generatorCache = new Map();
90
85
  * @returns {number[]}
91
86
  */
92
87
  function cachedGenerator(eccLen, field, base) {
93
- const key = `${field.name}|${eccLen}|${base}`;
94
- let g = generatorCache.get(key);
95
- if (!g) {
96
- g = generatorPoly(eccLen, field, base);
97
- generatorCache.set(key, g);
98
- }
99
- return g;
88
+ const key = `${field.name}|${eccLen}|${base}`;
89
+ let g = generatorCache.get(key);
90
+ if (!g) {
91
+ g = generatorPoly(eccLen, field, base);
92
+ generatorCache.set(key, g);
93
+ }
94
+ return g;
100
95
  }
101
-
102
96
  /**
103
97
  * Compute `eccLen` parity symbols for `data`.
104
98
  *
@@ -109,34 +103,34 @@ function cachedGenerator(eccLen, field, base) {
109
103
  * @returns {number[]} The parity symbols alone, length eccLen.
110
104
  */
111
105
  export function rsEncode(data, eccLen, field, base = 0) {
112
- if (eccLen <= 0) return [];
113
- const gen = cachedGenerator(eccLen, field, base);
114
- const res = new Array(data.length + eccLen).fill(0);
115
- for (let i = 0; i < data.length; i++) res[i] = data[i];
116
-
117
- // Synthetic division by a monic divisor: the leading coefficient is
118
- // annihilated each step and its multiple subtracted from the tail.
119
- for (let i = 0; i < data.length; i++) {
120
- const coef = res[i];
121
- if (coef === 0) continue;
122
- for (let j = 1; j <= eccLen; j++) {
123
- res[i + j] = field.sub(res[i + j], field.mul(gen[j], coef));
106
+ if (eccLen <= 0)
107
+ return [];
108
+ const gen = cachedGenerator(eccLen, field, base);
109
+ const res = new Array(data.length + eccLen).fill(0);
110
+ for (let i = 0; i < data.length; i++)
111
+ res[i] = data[i];
112
+ // Synthetic division by a monic divisor: the leading coefficient is
113
+ // annihilated each step and its multiple subtracted from the tail.
114
+ for (let i = 0; i < data.length; i++) {
115
+ const coef = res[i];
116
+ if (coef === 0)
117
+ continue;
118
+ for (let j = 1; j <= eccLen; j++) {
119
+ res[i + j] = field.sub(res[i + j], field.mul(gen[j], coef));
120
+ }
124
121
  }
125
- }
126
-
127
- const remainder = res.slice(data.length);
128
-
129
- // The codeword is data(x)*x^eccLen MINUS the remainder, so the parity
130
- // symbols are the negated remainder. In a binary field negation is the
131
- // identity and this is invisible; in GF(929) omitting it produces a
132
- // codeword that is not divisible by the generator, and every symbol fails
133
- // to decode. Exactly the class of bug the field abstraction exists to stop.
134
- if (field.prime) {
135
- for (let i = 0; i < remainder.length; i++) remainder[i] = field.neg(remainder[i]);
136
- }
137
- return remainder;
122
+ const remainder = res.slice(data.length);
123
+ // The codeword is data(x)*x^eccLen MINUS the remainder, so the parity
124
+ // symbols are the negated remainder. In a binary field negation is the
125
+ // identity and this is invisible; in GF(929) omitting it produces a
126
+ // codeword that is not divisible by the generator, and every symbol fails
127
+ // to decode. Exactly the class of bug the field abstraction exists to stop.
128
+ if (field.prime) {
129
+ for (let i = 0; i < remainder.length; i++)
130
+ remainder[i] = field.neg(remainder[i]);
131
+ }
132
+ return remainder;
138
133
  }
139
-
140
134
  /**
141
135
  * Evaluate a degree-descending polynomial at x (Horner).
142
136
  *
@@ -146,51 +140,57 @@ export function rsEncode(data, eccLen, field, base = 0) {
146
140
  * @returns {number}
147
141
  */
148
142
  function evalPoly(poly, x, field) {
149
- let acc = 0;
150
- for (let i = 0; i < poly.length; i++) {
151
- acc = field.add(field.mul(acc, x), poly[i]);
152
- }
153
- return acc;
143
+ let acc = 0;
144
+ for (let i = 0; i < poly.length; i++) {
145
+ acc = field.add(field.mul(acc, x), poly[i]);
146
+ }
147
+ return acc;
154
148
  }
155
-
156
149
  function multiplyAscending(left, right, field, limit) {
157
- const out = new Array(Math.min(limit, left.length + right.length - 1)).fill(0);
158
- for (let i = 0; i < left.length; i++) for (let j = 0; j < right.length && i + j < out.length; j++) {
159
- out[i + j] = field.add(out[i + j], field.mul(left[i], right[j]));
160
- }
161
- return out;
150
+ const out = new Array(Math.min(limit, left.length + right.length - 1)).fill(0);
151
+ for (let i = 0; i < left.length; i++)
152
+ for (let j = 0; j < right.length && i + j < out.length; j++) {
153
+ out[i + j] = field.add(out[i + j], field.mul(left[i], right[j]));
154
+ }
155
+ return out;
162
156
  }
163
-
164
157
  function berlekampMassey(syndromes, field) {
165
- const limit = syndromes.length;
166
- const lambda = new Array(limit + 1).fill(0);
167
- const previous = new Array(limit + 1).fill(0);
168
- const temporary = new Array(limit + 1).fill(0);
169
- lambda[0] = 1;
170
- previous[0] = 1;
171
- let errorCount = 0;
172
- let shift = 1;
173
- let lastDiscrepancy = 1;
174
-
175
- for (let step = 0; step < limit; step++) {
176
- let discrepancy = syndromes[step];
177
- for (let i = 1; i <= errorCount; i++) discrepancy = field.add(discrepancy, field.mul(lambda[i], syndromes[step - i]));
178
- if (discrepancy === 0) { shift++; continue; }
179
- const scale = field.div(discrepancy, lastDiscrepancy);
180
- for (let i = 0; i <= limit; i++) temporary[i] = lambda[i];
181
- for (let i = 0; i + shift <= limit; i++) if (previous[i] !== 0) {
182
- lambda[i + shift] = field.sub(lambda[i + shift], field.mul(scale, previous[i]));
158
+ const limit = syndromes.length;
159
+ const lambda = new Array(limit + 1).fill(0);
160
+ const previous = new Array(limit + 1).fill(0);
161
+ const temporary = new Array(limit + 1).fill(0);
162
+ lambda[0] = 1;
163
+ previous[0] = 1;
164
+ let errorCount = 0;
165
+ let shift = 1;
166
+ let lastDiscrepancy = 1;
167
+ for (let step = 0; step < limit; step++) {
168
+ let discrepancy = syndromes[step];
169
+ for (let i = 1; i <= errorCount; i++)
170
+ discrepancy = field.add(discrepancy, field.mul(lambda[i], syndromes[step - i]));
171
+ if (discrepancy === 0) {
172
+ shift++;
173
+ continue;
174
+ }
175
+ const scale = field.div(discrepancy, lastDiscrepancy);
176
+ for (let i = 0; i <= limit; i++)
177
+ temporary[i] = lambda[i];
178
+ for (let i = 0; i + shift <= limit; i++)
179
+ if (previous[i] !== 0) {
180
+ lambda[i + shift] = field.sub(lambda[i + shift], field.mul(scale, previous[i]));
181
+ }
182
+ if (2 * errorCount <= step) {
183
+ errorCount = step + 1 - errorCount;
184
+ for (let i = 0; i <= limit; i++)
185
+ previous[i] = temporary[i];
186
+ lastDiscrepancy = discrepancy;
187
+ shift = 1;
188
+ }
189
+ else
190
+ shift++;
183
191
  }
184
- if (2 * errorCount <= step) {
185
- errorCount = step + 1 - errorCount;
186
- for (let i = 0; i <= limit; i++) previous[i] = temporary[i];
187
- lastDiscrepancy = discrepancy;
188
- shift = 1;
189
- } else shift++;
190
- }
191
- return { locator: lambda.slice(0, errorCount + 1), errorCount };
192
+ return { locator: lambda.slice(0, errorCount + 1), errorCount };
192
193
  }
193
-
194
194
  /**
195
195
  * Correct errors in a received codeword, in place.
196
196
  *
@@ -203,125 +203,116 @@ function berlekampMassey(syndromes, field) {
203
203
  * @throws {ChecksumError} If the damage exceeds the correction capacity.
204
204
  */
205
205
  export function rsDecode(received, eccLen, field, base = 0, erasures = []) {
206
- const n = received.length;
207
- if (!Array.isArray(erasures) || new Set(erasures).size !== erasures.length || erasures.some((index) => !Number.isInteger(index) || index < 0 || index >= n)) {
208
- throw new ChecksumError('Reed-Solomon: erasure positions must be unique codeword indexes');
209
- }
210
- if (erasures.length > eccLen) throw new ChecksumError(`Reed-Solomon: ${erasures.length} erasures exceeds correction capacity ${eccLen} (${field.name})`);
211
-
212
- // --- Syndromes. S[i] = R(a^(base+i)); all zero means an intact codeword.
213
- const syn = new Array(eccLen).fill(0);
214
- let damaged = false;
215
- for (let i = 0; i < eccLen; i++) {
216
- const s = evalPoly(received, field.exp(base + i), field);
217
- syn[i] = s;
218
- if (s !== 0) damaged = true;
219
- }
220
- if (!damaged) return 0;
221
-
222
- // Remove the known roots before locating unknown errors. The leading
223
- // erasureCount terms contain only the known-location transient and are not
224
- // part of the error-only recurrence.
225
- let erasureLocator = [1];
226
- for (const index of erasures) {
227
- const location = field.exp(n - 1 - index);
228
- erasureLocator = multiplyAscending(erasureLocator, [1, field.neg(location)], field, eccLen + 1);
229
- }
230
- const modified = multiplyAscending(syn, erasureLocator, field, eccLen).slice(erasures.length);
231
- const { locator: errorLocator, errorCount } = berlekampMassey(modified, field);
232
- if (2 * errorCount + erasures.length > eccLen) {
233
- throw new ChecksumError(
234
- `Reed-Solomon: ${errorCount} errors and ${erasures.length} erasures exceed correction capacity ` +
235
- `${eccLen} (${field.name})`
236
- );
237
- }
238
- const lambda = multiplyAscending(erasureLocator, errorLocator, field, eccLen + 1);
239
- const totalCount = errorCount + erasures.length;
240
-
241
- // --- Chien search. Position p (counted from the low-order end) is in error
242
- // when lambda(a^-p) == 0.
243
- const positions = [];
244
- for (let p = 0; p < n; p++) {
245
- const xInv = field.exp(-p);
246
- let acc = 0;
247
- let term = 1;
248
- for (let i = 0; i <= totalCount; i++) {
249
- acc = field.add(acc, field.mul(lambda[i], term));
250
- term = field.mul(term, xInv);
251
- }
252
- if (acc === 0) positions.push(p);
253
- }
254
-
255
- if (positions.length !== totalCount) {
256
- throw new ChecksumError(
257
- `Reed-Solomon: located ${positions.length} of ${totalCount} error positions`
258
- );
259
- }
260
-
261
- // --- Error evaluator. omega(x) = [S(x) * lambda(x)] mod x^eccLen,
262
- // with S degree-ascending.
263
- const omega = new Array(eccLen).fill(0);
264
- for (let i = 0; i < eccLen; i++) {
265
- let acc = 0;
266
- for (let j = 0; j <= i && j <= totalCount; j++) {
267
- acc = field.add(acc, field.mul(lambda[j], syn[i - j]));
206
+ const n = received.length;
207
+ if (!Array.isArray(erasures) || new Set(erasures).size !== erasures.length || erasures.some((index) => !Number.isInteger(index) || index < 0 || index >= n)) {
208
+ throw new ChecksumError('Reed-Solomon: erasure positions must be unique codeword indexes');
268
209
  }
269
- omega[i] = acc;
270
- }
271
-
272
- // --- Forney. For an error at position p, with X = a^p:
273
- // magnitude = -X^(1-base) * omega(X^-1) / lambda'(X^-1)
274
- // The sign is a no-op in binary fields and load-bearing in GF(929).
275
- let corrected = 0;
276
- for (const p of positions) {
277
- const xInv = field.exp(-p);
278
-
279
- let num = 0;
280
- let term = 1;
210
+ if (erasures.length > eccLen)
211
+ throw new ChecksumError(`Reed-Solomon: ${erasures.length} erasures exceeds correction capacity ${eccLen} (${field.name})`);
212
+ // --- Syndromes. S[i] = R(a^(base+i)); all zero means an intact codeword.
213
+ const syn = new Array(eccLen).fill(0);
214
+ let damaged = false;
281
215
  for (let i = 0; i < eccLen; i++) {
282
- num = field.add(num, field.mul(omega[i], term));
283
- term = field.mul(term, xInv);
216
+ const s = evalPoly(received, field.exp(base + i), field);
217
+ syn[i] = s;
218
+ if (s !== 0)
219
+ damaged = true;
220
+ }
221
+ if (!damaged)
222
+ return 0;
223
+ // Remove the known roots before locating unknown errors. The leading
224
+ // erasureCount terms contain only the known-location transient and are not
225
+ // part of the error-only recurrence.
226
+ let erasureLocator = [1];
227
+ for (const index of erasures) {
228
+ const location = field.exp(n - 1 - index);
229
+ erasureLocator = multiplyAscending(erasureLocator, [1, field.neg(location)], field, eccLen + 1);
284
230
  }
285
-
286
- // Formal derivative: only odd-index terms survive in a binary field, but
287
- // in a prime field every term contributes with an integer multiplier.
288
- let den = 0;
289
- term = 1;
290
- for (let i = 1; i <= totalCount; i++) {
291
- if (field.prime) {
292
- // i * lambda[i] * x^(i-1), where `i` is repeated addition.
293
- let mult = 0;
294
- const t = field.mul(lambda[i], term);
295
- for (let k = 0; k < i; k++) mult = field.add(mult, t);
296
- den = field.add(den, mult);
297
- } else if (i % 2 === 1) {
298
- den = field.add(den, field.mul(lambda[i], term));
299
- }
300
- term = field.mul(term, xInv);
231
+ const modified = multiplyAscending(syn, erasureLocator, field, eccLen).slice(erasures.length);
232
+ const { locator: errorLocator, errorCount } = berlekampMassey(modified, field);
233
+ if (2 * errorCount + erasures.length > eccLen) {
234
+ throw new ChecksumError(`Reed-Solomon: ${errorCount} errors and ${erasures.length} erasures exceed correction capacity ` +
235
+ `${eccLen} (${field.name})`);
301
236
  }
302
-
303
- if (den === 0) {
304
- throw new ChecksumError('Reed-Solomon: singular error locator derivative');
237
+ const lambda = multiplyAscending(erasureLocator, errorLocator, field, eccLen + 1);
238
+ const totalCount = errorCount + erasures.length;
239
+ // --- Chien search. Position p (counted from the low-order end) is in error
240
+ // when lambda(a^-p) == 0.
241
+ const positions = [];
242
+ for (let p = 0; p < n; p++) {
243
+ const xInv = field.exp(-p);
244
+ let acc = 0;
245
+ let term = 1;
246
+ for (let i = 0; i <= totalCount; i++) {
247
+ acc = field.add(acc, field.mul(lambda[i], term));
248
+ term = field.mul(term, xInv);
249
+ }
250
+ if (acc === 0)
251
+ positions.push(p);
305
252
  }
306
-
307
- let magnitude = field.div(num, den);
308
- // X^(1-base): one factor of X when base is 0, none when base is 1.
309
- if (base === 0) magnitude = field.mul(magnitude, field.exp(p));
310
- else if (base !== 1) magnitude = field.mul(magnitude, field.exp(p * (1 - base)));
311
- magnitude = field.neg(magnitude);
312
-
313
- const idx = n - 1 - p;
314
- received[idx] = field.sub(received[idx], magnitude);
315
- corrected++;
316
- }
317
-
318
- // Verify: a genuine correction zeroes every syndrome. Without this check,
319
- // damage beyond capacity can produce a plausible-looking wrong answer.
320
- for (let i = 0; i < eccLen; i++) {
321
- if (evalPoly(received, field.exp(base + i), field) !== 0) {
322
- throw new ChecksumError('Reed-Solomon: correction failed verification');
253
+ if (positions.length !== totalCount) {
254
+ throw new ChecksumError(`Reed-Solomon: located ${positions.length} of ${totalCount} error positions`);
255
+ }
256
+ // --- Error evaluator. omega(x) = [S(x) * lambda(x)] mod x^eccLen,
257
+ // with S degree-ascending.
258
+ const omega = new Array(eccLen).fill(0);
259
+ for (let i = 0; i < eccLen; i++) {
260
+ let acc = 0;
261
+ for (let j = 0; j <= i && j <= totalCount; j++) {
262
+ acc = field.add(acc, field.mul(lambda[j], syn[i - j]));
263
+ }
264
+ omega[i] = acc;
265
+ }
266
+ // --- Forney. For an error at position p, with X = a^p:
267
+ // magnitude = -X^(1-base) * omega(X^-1) / lambda'(X^-1)
268
+ // The sign is a no-op in binary fields and load-bearing in GF(929).
269
+ let corrected = 0;
270
+ for (const p of positions) {
271
+ const xInv = field.exp(-p);
272
+ let num = 0;
273
+ let term = 1;
274
+ for (let i = 0; i < eccLen; i++) {
275
+ num = field.add(num, field.mul(omega[i], term));
276
+ term = field.mul(term, xInv);
277
+ }
278
+ // Formal derivative: only odd-index terms survive in a binary field, but
279
+ // in a prime field every term contributes with an integer multiplier.
280
+ let den = 0;
281
+ term = 1;
282
+ for (let i = 1; i <= totalCount; i++) {
283
+ if (field.prime) {
284
+ // i * lambda[i] * x^(i-1), where `i` is repeated addition.
285
+ let mult = 0;
286
+ const t = field.mul(lambda[i], term);
287
+ for (let k = 0; k < i; k++)
288
+ mult = field.add(mult, t);
289
+ den = field.add(den, mult);
290
+ }
291
+ else if (i % 2 === 1) {
292
+ den = field.add(den, field.mul(lambda[i], term));
293
+ }
294
+ term = field.mul(term, xInv);
295
+ }
296
+ if (den === 0) {
297
+ throw new ChecksumError('Reed-Solomon: singular error locator derivative');
298
+ }
299
+ let magnitude = field.div(num, den);
300
+ // X^(1-base): one factor of X when base is 0, none when base is 1.
301
+ if (base === 0)
302
+ magnitude = field.mul(magnitude, field.exp(p));
303
+ else if (base !== 1)
304
+ magnitude = field.mul(magnitude, field.exp(p * (1 - base)));
305
+ magnitude = field.neg(magnitude);
306
+ const idx = n - 1 - p;
307
+ received[idx] = field.sub(received[idx], magnitude);
308
+ corrected++;
309
+ }
310
+ // Verify: a genuine correction zeroes every syndrome. Without this check,
311
+ // damage beyond capacity can produce a plausible-looking wrong answer.
312
+ for (let i = 0; i < eccLen; i++) {
313
+ if (evalPoly(received, field.exp(base + i), field) !== 0) {
314
+ throw new ChecksumError('Reed-Solomon: correction failed verification');
315
+ }
323
316
  }
324
- }
325
-
326
- return corrected;
317
+ return corrected;
327
318
  }