@@ -76,7 +76,11 @@ pub trait CorrectableError {
76
76
return None ;
77
77
}
78
78
79
- self . residue_error ( ) . map ( |e| Corrector { residue : e. residue ( ) , phantom : PhantomData } )
79
+ self . residue_error ( ) . map ( |e| Corrector {
80
+ erasures : FieldVec :: new ( ) ,
81
+ residue : e. residue ( ) ,
82
+ phantom : PhantomData ,
83
+ } )
80
84
}
81
85
}
82
86
@@ -127,12 +131,40 @@ impl CorrectableError for DecodeError {
127
131
}
128
132
129
133
/// An error-correction context.
130
- pub struct Corrector < Ck > {
134
+ pub struct Corrector < Ck : Checksum > {
135
+ erasures : FieldVec < usize > ,
131
136
residue : Polynomial < Fe32 > ,
132
137
phantom : PhantomData < Ck > ,
133
138
}
134
139
135
140
impl < Ck : Checksum > Corrector < Ck > {
141
+ /// A bound on the number of errors and erasures (errors with known location)
142
+ /// can be corrected by this corrector.
143
+ ///
144
+ /// Returns N such that, given E errors and X erasures, corection is possible
145
+ /// iff 2E + X <= N.
146
+ pub fn singleton_bound ( & self ) -> usize {
147
+ // d - 1, where d = [number of consecutive roots] + 2
148
+ Ck :: ROOT_EXPONENTS . end ( ) - Ck :: ROOT_EXPONENTS . start ( ) + 1
149
+ }
150
+
151
+ /// TODO
152
+ pub fn add_erasures ( & mut self , locs : & [ usize ] ) {
153
+ for loc in locs {
154
+ // If the user tries to add too many erasures, just ignore them. In
155
+ // this case error correction is guaranteed to fail anyway, because
156
+ // they will have exceeded the singleton bound. (Otherwise, the
157
+ // singleton bound, which is always <= the checksum length, must be
158
+ // greater than NO_ALLOC_MAX_LENGTH. So the checksum length must be
159
+ // greater than NO_ALLOC_MAX_LENGTH. Then correction will still fail.)
160
+ #[ cfg( not( feature = "alloc" ) ) ]
161
+ if self . erasures . len ( ) == NO_ALLOC_MAX_LENGTH {
162
+ break ;
163
+ }
164
+ self . erasures . push ( * loc) ;
165
+ }
166
+ }
167
+
136
168
/// Returns an iterator over the errors in the string.
137
169
///
138
170
/// Returns `None` if it can be determined that there are too many errors to be
@@ -145,29 +177,44 @@ impl<Ck: Checksum> Corrector<Ck> {
145
177
/// string may not actually be the intended string.
146
178
pub fn bch_errors ( & self ) -> Option < ErrorIterator < Ck > > {
147
179
// 1. Compute all syndromes by evaluating the residue at each power of the generator.
148
- let syndromes: FieldVec < _ > = Ck :: ROOT_GENERATOR
180
+ let syndromes: Polynomial < _ > = Ck :: ROOT_GENERATOR
149
181
. powers_range ( Ck :: ROOT_EXPONENTS )
150
182
. map ( |rt| self . residue . evaluate ( & rt) )
151
183
. collect ( ) ;
152
184
185
+ // 1a. Compute the "Forney syndrome polynomial" which is the product of the syndrome
186
+ // polynomial and the erasure locator. This "erases the erasures" so that B-M
187
+ // can find only the errors.
188
+ let mut erasure_locator = Polynomial :: with_monic_leading_term ( & [ ] ) ; // 1
189
+ for loc in & self . erasures {
190
+ let factor: Polynomial < _ > =
191
+ [ Ck :: CorrectionField :: ONE , -Ck :: ROOT_GENERATOR . powi ( * loc as i64 ) ]
192
+ . iter ( )
193
+ . cloned ( )
194
+ . collect ( ) ; // alpha^-ix - 1
195
+ erasure_locator = erasure_locator. mul_mod_x_d ( & factor, usize:: MAX ) ;
196
+ }
197
+ let forney_syndromes = erasure_locator. convolution ( & syndromes) ;
198
+
153
199
// 2. Use the Berlekamp-Massey algorithm to find the connection polynomial of the
154
200
// LFSR that generates these syndromes. For magical reasons this will be equal
155
201
// to the error locator polynomial for the syndrome.
156
- let lfsr = LfsrIter :: berlekamp_massey ( & syndromes [ ..] ) ;
202
+ let lfsr = LfsrIter :: berlekamp_massey ( & forney_syndromes . as_inner ( ) [ ..] ) ;
157
203
let conn = lfsr. coefficient_polynomial ( ) ;
158
204
159
205
// 3. The connection polynomial is the error locator polynomial. Use this to get
160
206
// the errors.
161
- let max_correctable_errors =
162
- ( Ck :: ROOT_EXPONENTS . end ( ) - Ck :: ROOT_EXPONENTS . start ( ) + 1 ) / 2 ;
163
- if conn. degree ( ) <= max_correctable_errors {
207
+ if erasure_locator. degree ( ) + 2 * conn. degree ( ) <= self . singleton_bound ( ) {
208
+ // 3a. Compute the "errata locator" which is the product of the error locator
209
+ // and the erasure locator. Note that while we used the Forney syndromes
210
+ // when calling the BM algorithm, in all other cases we use the ordinary
211
+ // unmodified syndromes.
212
+ let errata_locator = conn. mul_mod_x_d ( & erasure_locator, usize:: MAX ) ;
164
213
Some ( ErrorIterator {
165
- evaluator : conn. mul_mod_x_d (
166
- & Polynomial :: from ( syndromes) ,
167
- Ck :: ROOT_EXPONENTS . end ( ) - Ck :: ROOT_EXPONENTS . start ( ) + 1 ,
168
- ) ,
169
- locator_derivative : conn. formal_derivative ( ) ,
170
- inner : conn. find_nonzero_distinct_roots ( Ck :: ROOT_GENERATOR ) ,
214
+ evaluator : errata_locator. mul_mod_x_d ( & syndromes, self . singleton_bound ( ) ) ,
215
+ locator_derivative : errata_locator. formal_derivative ( ) ,
216
+ erasures : & self . erasures [ ..] ,
217
+ errors : conn. find_nonzero_distinct_roots ( Ck :: ROOT_GENERATOR ) ,
171
218
a : Ck :: ROOT_GENERATOR ,
172
219
c : * Ck :: ROOT_EXPONENTS . start ( ) ,
173
220
} )
@@ -206,32 +253,39 @@ impl<Ck: Checksum> Corrector<Ck> {
206
253
/// caller should fix this before attempting error correction. If it is unknown,
207
254
/// the caller cannot assume anything about the intended checksum, and should not
208
255
/// attempt error correction.
209
- pub struct ErrorIterator < Ck : Checksum > {
256
+ pub struct ErrorIterator < ' c , Ck : Checksum > {
210
257
evaluator : Polynomial < Ck :: CorrectionField > ,
211
258
locator_derivative : Polynomial < Ck :: CorrectionField > ,
212
- inner : super :: polynomial:: RootIter < Ck :: CorrectionField > ,
259
+ erasures : & ' c [ usize ] ,
260
+ errors : super :: polynomial:: RootIter < Ck :: CorrectionField > ,
213
261
a : Ck :: CorrectionField ,
214
262
c : usize ,
215
263
}
216
264
217
- impl < Ck : Checksum > Iterator for ErrorIterator < Ck > {
265
+ impl < ' c , Ck : Checksum > Iterator for ErrorIterator < ' c , Ck > {
218
266
type Item = ( usize , Fe32 ) ;
219
267
220
268
fn next ( & mut self ) -> Option < Self :: Item > {
221
269
// Compute -i, which is the location we will return to the user.
222
- let neg_i = match self . inner . next ( ) {
223
- None => return None ,
224
- Some ( 0 ) => 0 ,
225
- Some ( x) => Ck :: ROOT_GENERATOR . multiplicative_order ( ) - x,
270
+ let neg_i = if self . erasures . is_empty ( ) {
271
+ match self . errors . next ( ) {
272
+ None => return None ,
273
+ Some ( 0 ) => 0 ,
274
+ Some ( x) => Ck :: ROOT_GENERATOR . multiplicative_order ( ) - x,
275
+ }
276
+ } else {
277
+ let pop = self . erasures [ 0 ] ;
278
+ self . erasures = & self . erasures [ 1 ..] ;
279
+ pop
226
280
} ;
227
281
228
282
// Forney's equation, as described in https://en.wikipedia.org/wiki/BCH_code#Forney_algorithm
229
283
//
230
284
// It is rendered as
231
285
//
232
- // a^i evaluator(a^-i)
233
- // e_k = - ---------------------------------
234
- // a^(ci ) locator_derivative(a^-i)
286
+ // evaluator(a^-i)
287
+ // e_k = - -----------------------------------------
288
+ // (a^i)^(c - 1) ) locator_derivative(a^-i)
235
289
//
236
290
// where here a is `Ck::ROOT_GENERATOR`, c is the first element of the range
237
291
// `Ck::ROOT_EXPONENTS`, and both evalutor and locator_derivative are polynomials
@@ -240,8 +294,8 @@ impl<Ck: Checksum> Iterator for ErrorIterator<Ck> {
240
294
let a_i = self . a . powi ( neg_i as i64 ) ;
241
295
let a_neg_i = a_i. clone ( ) . multiplicative_inverse ( ) ;
242
296
243
- let num = self . evaluator . evaluate ( & a_neg_i) * & a_i ;
244
- let den = a_i. powi ( self . c as i64 ) * self . locator_derivative . evaluate ( & a_neg_i) ;
297
+ let num = self . evaluator . evaluate ( & a_neg_i) ;
298
+ let den = a_i. powi ( self . c as i64 - 1 ) * self . locator_derivative . evaluate ( & a_neg_i) ;
245
299
let ret = -num / den;
246
300
match ret. try_into ( ) {
247
301
Ok ( ret) => Some ( ( neg_i, ret) ) ,
@@ -263,9 +317,13 @@ mod tests {
263
317
match SegwitHrpstring :: new ( s) {
264
318
Ok ( _) => panic ! ( "{} successfully, and wrongly, parsed" , s) ,
265
319
Err ( e) => {
266
- let ctx = e. correction_context :: < Bech32 > ( ) . unwrap ( ) ;
320
+ let mut ctx = e. correction_context :: < Bech32 > ( ) . unwrap ( ) ;
267
321
let mut iter = ctx. bch_errors ( ) . unwrap ( ) ;
322
+ assert_eq ! ( iter. next( ) , Some ( ( 0 , Fe32 :: X ) ) ) ;
323
+ assert_eq ! ( iter. next( ) , None ) ;
268
324
325
+ ctx. add_erasures ( & [ 0 ] ) ;
326
+ let mut iter = ctx. bch_errors ( ) . unwrap ( ) ;
269
327
assert_eq ! ( iter. next( ) , Some ( ( 0 , Fe32 :: X ) ) ) ;
270
328
assert_eq ! ( iter. next( ) , None ) ;
271
329
}
@@ -276,9 +334,13 @@ mod tests {
276
334
match SegwitHrpstring :: new ( s) {
277
335
Ok ( _) => panic ! ( "{} successfully, and wrongly, parsed" , s) ,
278
336
Err ( e) => {
279
- let ctx = e. correction_context :: < Bech32 > ( ) . unwrap ( ) ;
337
+ let mut ctx = e. correction_context :: < Bech32 > ( ) . unwrap ( ) ;
280
338
let mut iter = ctx. bch_errors ( ) . unwrap ( ) ;
339
+ assert_eq ! ( iter. next( ) , Some ( ( 6 , Fe32 :: T ) ) ) ;
340
+ assert_eq ! ( iter. next( ) , None ) ;
281
341
342
+ ctx. add_erasures ( & [ 6 ] ) ;
343
+ let mut iter = ctx. bch_errors ( ) . unwrap ( ) ;
282
344
assert_eq ! ( iter. next( ) , Some ( ( 6 , Fe32 :: T ) ) ) ;
283
345
assert_eq ! ( iter. next( ) , None ) ;
284
346
}
@@ -297,13 +359,42 @@ mod tests {
297
359
}
298
360
}
299
361
300
- // Two errors.
301
- let s = "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mxx " ;
362
+ // Two errors; cannot correct .
363
+ let s = "bc1qar0srrr7xfkvy5l64qlydnw9re59gtzzwf5mdx " ;
302
364
match SegwitHrpstring :: new ( s) {
303
365
Ok ( _) => panic ! ( "{} successfully, and wrongly, parsed" , s) ,
304
366
Err ( e) => {
305
- let ctx = e. correction_context :: < Bech32 > ( ) . unwrap ( ) ;
367
+ let mut ctx = e. correction_context :: < Bech32 > ( ) . unwrap ( ) ;
306
368
assert ! ( ctx. bch_errors( ) . is_none( ) ) ;
369
+
370
+ // But we can correct it if we inform where an error is.
371
+ ctx. add_erasures ( & [ 0 ] ) ;
372
+ let mut iter = ctx. bch_errors ( ) . unwrap ( ) ;
373
+ assert_eq ! ( iter. next( ) , Some ( ( 0 , Fe32 :: X ) ) ) ;
374
+ assert_eq ! ( iter. next( ) , Some ( ( 20 , Fe32 :: _3) ) ) ;
375
+ assert_eq ! ( iter. next( ) , None ) ;
376
+
377
+ ctx. add_erasures ( & [ 20 ] ) ;
378
+ let mut iter = ctx. bch_errors ( ) . unwrap ( ) ;
379
+ assert_eq ! ( iter. next( ) , Some ( ( 0 , Fe32 :: X ) ) ) ;
380
+ assert_eq ! ( iter. next( ) , Some ( ( 20 , Fe32 :: _3) ) ) ;
381
+ assert_eq ! ( iter. next( ) , None ) ;
382
+ }
383
+ }
384
+
385
+ // In fact, if we know the locations, we can correct up to 3 errors.
386
+ let s = "bc1q9r0srrr7xfkvy5l64qlydnw9re59gtzzwf5mdx" ;
387
+ match SegwitHrpstring :: new ( s) {
388
+ Ok ( _) => panic ! ( "{} successfully, and wrongly, parsed" , s) ,
389
+ Err ( e) => {
390
+ let mut ctx = e. correction_context :: < Bech32 > ( ) . unwrap ( ) ;
391
+ ctx. add_erasures ( & [ 37 , 0 , 20 ] ) ;
392
+ let mut iter = ctx. bch_errors ( ) . unwrap ( ) ;
393
+
394
+ assert_eq ! ( iter. next( ) , Some ( ( 37 , Fe32 :: C ) ) ) ;
395
+ assert_eq ! ( iter. next( ) , Some ( ( 0 , Fe32 :: X ) ) ) ;
396
+ assert_eq ! ( iter. next( ) , Some ( ( 20 , Fe32 :: _3) ) ) ;
397
+ assert_eq ! ( iter. next( ) , None ) ;
307
398
}
308
399
}
309
400
}
0 commit comments