40be310c1612884b06b8dcda52a80b9f962effa4
[zxing.git] / cpp / core / src / common / reedsolomon / ReedSolomonDecoder.cpp
1 /*
2  *  ReedSolomonDecoder.cpp
3  *  zxing
4  *
5  *  Created by Christian Brunschen on 05/05/2008.
6  *  Copyright 2008 Google UK. All rights reserved.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #include <iostream>
22
23 #include <memory>
24 #include "ReedSolomonDecoder.h"
25 #include "GF256.h"
26 #include "GF256Poly.h"
27 #include "ReedSolomonException.h"
28
29 using namespace std;
30
31 namespace reedsolomon {
32   ReedSolomonDecoder::ReedSolomonDecoder(GF256 &fld) : field(fld) {
33   }
34   
35   ReedSolomonDecoder::~ReedSolomonDecoder() {
36   }
37   
38   void ReedSolomonDecoder::decode(ArrayRef<int> received, int twoS) {
39 #ifdef DEBUG
40     cout << "decode(): received = " << &received << ", array = " <<
41       received.array_ << " (" << received.array_->count_ << ")\n";
42 #endif
43     Ref<GF256Poly> poly(new GF256Poly(field, received));
44     cout << "decoding with poly " << *poly << "\n";
45     ArrayRef<int> syndromeCoefficients(new Array<int>(twoS));
46 #ifdef DEBUG
47     cout << "syndromeCoefficients array = " << 
48       syndromeCoefficients.array_ << "\n";
49 #endif
50     bool noError = true;
51     for (int i = 0; i < twoS; i++) {
52       int eval = poly->evaluateAt(field.exp(i));
53       syndromeCoefficients[syndromeCoefficients->size() - 1 - i] = eval;
54       if (eval != 0) {
55         noError = false;
56       }
57     }
58     if (noError) {
59 #ifdef DEBUG
60       cout << "before returning: syndromeCoefficients rc = " << 
61         syndromeCoefficients.array_->count_ << "\n";
62 #endif
63       return;
64     }
65     
66 #ifdef DEBUG
67     cout << "syndromeCoefficients rc = " << 
68       syndromeCoefficients.array_->count_ << "\n";
69 #endif
70     Ref<GF256Poly> syndrome(new GF256Poly(field, syndromeCoefficients));
71 #ifdef DEBUG
72     cout << "syndrome rc = " << syndrome.object_->count_ << 
73       ", syndromeCoefficients rc = " << 
74       syndromeCoefficients.array_->count_ << "\n";
75 #endif
76     Ref<GF256Poly> monomial(field.buildMonomial(twoS, 1));
77 #ifdef DEBUG
78     cout << "monopmial rc = " << monomial.object_->count_ << "\n";
79 #endif
80     vector<Ref<GF256Poly> > sigmaOmega
81       (runEuclideanAlgorithm(monomial, syndrome, twoS));
82     ArrayRef<int> errorLocations = findErrorLocations(sigmaOmega[0]);
83 #ifdef DEBUG
84     cout << "errorLocations count: " << errorLocations.array_->count_ << "\n";
85 #endif
86     ArrayRef<int> errorMagitudes = findErrorMagnitudes(sigmaOmega[1],
87                                                        errorLocations);
88     for (unsigned i = 0; i < errorLocations->size(); i++) {
89       int position = received->size() - 1 - field.log(errorLocations[i]);
90       received[position] = GF256::addOrSubtract(received[position],
91                                                 errorMagitudes[i]);
92     }
93   }
94   
95   vector<Ref<GF256Poly> > ReedSolomonDecoder::
96   runEuclideanAlgorithm(Ref<GF256Poly> a,
97                         Ref<GF256Poly> b,
98                         int R) {
99     // Assume a's degree is >= b's
100     if (a->getDegree() < b->getDegree()) {
101       Ref<GF256Poly> tmp = a;
102       a = b;
103       b = tmp;
104     }
105     
106     Ref<GF256Poly> rLast(a);
107     Ref<GF256Poly> r(b);
108     Ref<GF256Poly> sLast(field.getOne());
109     Ref<GF256Poly> s(field.getZero());
110     Ref<GF256Poly> tLast(field.getZero());
111     Ref<GF256Poly> t(field.getOne());
112     
113     // Run Euclidean algorithm until r's degree is less than R/2
114     while (r->getDegree() >= R / 2) {
115       Ref<GF256Poly> rLastLast(rLast);
116       Ref<GF256Poly> sLastLast(sLast);
117       Ref<GF256Poly> tLastLast(tLast);
118       rLast = r;
119       sLast = s;
120       tLast = t;
121       
122       // Divide rLastLast by rLast, with quotient q and remainder r
123       if (rLast->isZero()) {
124         // Oops, Euclidean algorithm already terminated?
125         throw new ReedSolomonException("r_{i-1} was zero");
126       }
127       r = rLastLast;
128       Ref<GF256Poly> q(field.getZero());
129       int denominatorLeadingTerm = rLast->getCoefficient(rLast->getDegree());
130       int dltInverse = field.inverse(denominatorLeadingTerm);
131       while (r->getDegree() >= rLast->getDegree() && !r->isZero()) {
132         int degreeDiff = r->getDegree() - rLast->getDegree();
133         int scale = field.multiply(r->getCoefficient(r->getDegree()), 
134                                    dltInverse);
135         q = q->addOrSubtract(field.buildMonomial(degreeDiff, scale));
136         r = r->addOrSubtract(rLast->multiplyByMonomial(degreeDiff, scale));
137       }
138       
139       s = q->multiply(sLast)->addOrSubtract(sLastLast);
140       t = q->multiply(tLast)->addOrSubtract(tLastLast);
141     }
142     
143     int sigmaTildeAtZero = t->getCoefficient(0);
144     if (sigmaTildeAtZero == 0) {
145       throw new ReedSolomonException("sigmaTilde(0) was zero");
146     }
147     
148     int inverse = field.inverse(sigmaTildeAtZero);
149     Ref<GF256Poly> sigma(t->multiply(inverse));
150     Ref<GF256Poly> omega(r->multiply(inverse));
151     
152     cout << "t = " << *t << "\n";
153     cout << "r = " << *r << "\n";
154     cout << "sigma = " << *sigma << "\n";
155     cout << "omega = " << *omega << "\n";
156     
157     vector<Ref<GF256Poly> > result(2);
158     result[0] = sigma;
159     result[1] = omega;
160     return result;
161   }
162   
163   ArrayRef<int> ReedSolomonDecoder::
164   findErrorLocations(Ref<GF256Poly> errorLocator) {
165     // This is a direct application of Chien's search
166     int numErrors = errorLocator->getDegree();
167     if (numErrors == 1) { // shortcut
168       ArrayRef<int> result(1);
169       result[0] = errorLocator->getCoefficient(1);
170       return result;
171     }
172     ArrayRef<int> result(numErrors);
173     int e = 0;
174     for (int i = 1; i < 256 && e < numErrors; i++) {
175       // cout << "errorLocator(" << i << ") == " << errorLocator->evaluateAt(i) << "\n";
176       if (errorLocator->evaluateAt(i) == 0) {
177         result[e] = field.inverse(i);
178         e++;
179       }
180     }
181     if (e != numErrors) {
182       throw new ReedSolomonException
183         ("Error locator degree does not match number of roots");
184     }
185     return result;
186   }
187   
188   ArrayRef<int> ReedSolomonDecoder::
189   findErrorMagnitudes(Ref<GF256Poly> errorEvaluator,
190                       ArrayRef<int> errorLocations) {
191     // This is directly applying Forney's Formula
192     int s = errorLocations.size();
193     ArrayRef<int> result(s);
194     for (int i = 0; i < s; i++) {
195       int xiInverse = field.inverse(errorLocations[i]);
196       int denominator = 1;
197       for (int j = 0; j < s; j++) {
198         if (i != j) {
199           denominator = 
200             field.multiply(denominator,
201                            GF256::addOrSubtract
202                             (1, field.multiply(errorLocations[j], xiInverse)));
203         }
204       }
205       result[i] = field.multiply(errorEvaluator->evaluateAt(xiInverse),
206                                  field.inverse(denominator));
207     }
208     return result;
209   }
210 }