Workaround for Hotspot bug that lets tests run without -Xint, from Steven Parkes
[zxing.git] / core / src / com / google / zxing / common / reedsolomon / ReedSolomonDecoder.java
1 /*
2  * Copyright 2007 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.google.zxing.common.reedsolomon;
18
19 /**
20  * <p>Implements Reed-Solomon decoding, as the name implies.</p>
21  *
22  * <p>The algorithm will not be explained here, but the following references were helpful
23  * in creating this implementation:</p>
24  *
25  * <ul>
26  * <li>Bruce Maggs.
27  * <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
28  * "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
29  * <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
30  * "Chapter 5. Generalized Reed-Solomon Codes"</a>
31  * (see discussion of Euclidean algorithm)</li>
32  * </ul>
33  *
34  * <p>Much credit is due to William Rucklidge since portions of this code are an indirect
35  * port of his C++ Reed-Solomon implementation.</p>
36  *
37  * @author Sean Owen
38  * @author William Rucklidge
39  * @author sanfordsquires
40  */
41 public final class ReedSolomonDecoder {
42
43   private final GF256 field;
44
45   public ReedSolomonDecoder(GF256 field) {
46     this.field = field;
47   }
48
49   /**
50    * <p>Decodes given set of received codewords, which include both data and error-correction
51    * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
52    * in the input.</p>
53    *
54    * @param received data and error-correction codewords
55    * @param twoS number of error-correction codewords available
56    * @throws ReedSolomonException if decoding fails for any reason
57    */
58   public void decode(int[] received, int twoS) throws ReedSolomonException {
59     GF256Poly poly = new GF256Poly(field, received);
60     int[] syndromeCoefficients = new int[twoS];
61     boolean dataMatrix = field.equals(GF256.DATA_MATRIX_FIELD);
62     boolean noError = true;
63     for (int i = 0; i < twoS; i++) {
64       // Thanks to sanfordsquires for this fix:
65       int eval = poly.evaluateAt(field.exp(dataMatrix ? i + 1 : i));
66       syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval;
67       if (eval != 0) {
68         noError = false;
69       }
70     }
71     if (noError) {
72       return;
73     }
74     GF256Poly syndrome = new GF256Poly(field, syndromeCoefficients);
75     GF256Poly[] sigmaOmega =
76         runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
77     GF256Poly sigma = sigmaOmega[0];
78     GF256Poly omega = sigmaOmega[1];
79     int[] errorLocations = findErrorLocations(sigma);
80     int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations, dataMatrix);
81     for (int i = 0; i < errorLocations.length; i++) {
82       int position = received.length - 1 - field.log(errorLocations[i]);
83       if (position < 0) {
84         throw new ReedSolomonException("Bad error location");
85       }
86       received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[i]);
87     }
88   }
89
90   private GF256Poly[] runEuclideanAlgorithm(GF256Poly a, GF256Poly b, int R)
91       throws ReedSolomonException {
92     // Assume a's degree is >= b's
93     if (a.getDegree() < b.getDegree()) {
94       GF256Poly temp = a;
95       a = b;
96       b = temp;
97     }
98
99     GF256Poly rLast = a;
100     GF256Poly r = b;
101     GF256Poly sLast = field.getOne();
102     GF256Poly s = field.getZero();
103     GF256Poly tLast = field.getZero();
104     GF256Poly t = field.getOne();
105
106     // Run Euclidean algorithm until r's degree is less than R/2
107     while (r.getDegree() >= R / 2) {
108       GF256Poly rLastLast = rLast;
109       GF256Poly sLastLast = sLast;
110       GF256Poly tLastLast = tLast;
111       rLast = r;
112       sLast = s;
113       tLast = t;
114
115       // Divide rLastLast by rLast, with quotient in q and remainder in r
116       if (rLast.isZero()) {
117         // Oops, Euclidean algorithm already terminated?
118         throw new ReedSolomonException("r_{i-1} was zero");
119       }
120       r = rLastLast;
121       GF256Poly q = field.getZero();
122       int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
123       int dltInverse = field.inverse(denominatorLeadingTerm);
124       while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
125         int degreeDiff = r.getDegree() - rLast.getDegree();
126         int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);
127         q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));
128         r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
129       }
130
131       s = q.multiply(sLast).addOrSubtract(sLastLast);
132       t = q.multiply(tLast).addOrSubtract(tLastLast);
133     }
134
135     int sigmaTildeAtZero = t.getCoefficient(0);
136     if (sigmaTildeAtZero == 0) {
137       throw new ReedSolomonException("sigmaTilde(0) was zero");
138     }
139
140     int inverse = field.inverse(sigmaTildeAtZero);
141     GF256Poly sigma = t.multiply(inverse);
142     GF256Poly omega = r.multiply(inverse);
143     return new GF256Poly[]{sigma, omega};
144   }
145
146   private int[] findErrorLocations(GF256Poly errorLocator) throws ReedSolomonException {
147     // This is a direct application of Chien's search
148     int numErrors = errorLocator.getDegree();
149     if (numErrors == 1) { // shortcut
150       return new int[] { errorLocator.getCoefficient(1) };
151     }
152     int[] result = new int[numErrors];
153     int e = 0;
154     for (int i = 1; i < 256 && e < numErrors; i++) {
155       if (errorLocator.evaluateAt(i) == 0) {
156         result[e] = field.inverse(i);
157         e++;
158       }
159     }
160     if (e != numErrors) {
161       throw new ReedSolomonException("Error locator degree does not match number of roots");
162     }
163     return result;
164   }
165
166   private int[] findErrorMagnitudes(GF256Poly errorEvaluator, int[] errorLocations, boolean dataMatrix) {
167     // This is directly applying Forney's Formula
168     int s = errorLocations.length;
169     int[] result = new int[s];
170     for (int i = 0; i < s; i++) {
171       int xiInverse = field.inverse(errorLocations[i]);
172       int denominator = 1;
173       for (int j = 0; j < s; j++) {
174         if (i != j) {
175           //denominator = field.multiply(denominator,
176           //    GF256.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
177           // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
178           // Below is a funny-looking workaround from Steven Parkes
179           int term = field.multiply(errorLocations[j], xiInverse);
180           int termPlus1 = ((term & 0x1) == 0) ? (term | 1) : (term & ~1);
181           denominator = field.multiply(denominator, termPlus1);
182         }
183       }
184       result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),
185           field.inverse(denominator));
186       // Thanks to sanfordsquires for this fix:
187       if (dataMatrix) {
188         result[i] = field.multiply(result[i], xiInverse);
189       }
190     }
191     return result;
192   }
193
194 }