Refactored Reed-Solomon so it can be used with different GF(256) primitive polynomials

git-svn-id: https://zxing.googlecode.com/svn/trunk@209 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2008-02-19 16:14:34 +00:00
parent 833a8b03f7
commit cde4d22ece
5 changed files with 120 additions and 91 deletions

View file

@ -18,8 +18,7 @@ package com.google.zxing.common.reedsolomon;
/** /**
* <p>This class contains utility methods for performing mathematical operations over * <p>This class contains utility methods for performing mathematical operations over
* the Galois Field GF(256). Operations use the primitive polynomial * the Galois Field GF(256). Operations use a given primitive polynomial in calculations.</p>
* x^8 + x^4 + x^3 + x^2 + 1 in calculations.</p>
* *
* <p>Throughout this package, elements of GF(256) are represented as an <code>int</code> * <p>Throughout this package, elements of GF(256) are represented as an <code>int</code>
* for convenience and speed (but at the cost of memory). * for convenience and speed (but at the cost of memory).
@ -27,28 +26,63 @@ package com.google.zxing.common.reedsolomon;
* *
* @author srowen@google.com (Sean Owen) * @author srowen@google.com (Sean Owen)
*/ */
final class GF256 { public final class GF256 {
private static final int PRIMITIVE = 0x011D; public static final GF256 QR_CODE_FIELD = new GF256(0x011D); // x^8 + x^4 + x^3 + x^2 + 1
private static final int[] exp = new int[256]; public static final GF256 DATA_MATRIX_FIELD = new GF256(0x012D); // x^8 + x^5 + x^3 + x^2 + 1
private static final int[] log = new int[256];
static { private final int[] exp;
private final int[] log;
private final GF256Poly zero;
private final GF256Poly one;
/**
* Create a representation of GF(256) using the given primitive polynomial.
*
* @param primitive irreducible polynomial whose coefficients are represented by
* the bits of an int, where the least-significant bit represents the constant
* coefficient
*/
private GF256(int primitive) {
exp = new int[256];
log = new int[256];
int x = 1; int x = 1;
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
exp[i] = x; exp[i] = x;
x <<= 1; // x = x * 2; we're assuming the generator alpha is 2 x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
if (x >= 0x100) { if (x >= 0x100) {
x ^= PRIMITIVE; x ^= primitive;
} }
} }
for (int i = 0; i < 255; i++) { for (int i = 0; i < 255; i++) {
log[exp[i]] = i; log[exp[i]] = i;
} }
// log[0] == 0 but this should never be used // log[0] == 0 but this should never be used
zero = new GF256Poly(this, new int[]{0});
one = new GF256Poly(this, new int[]{1});
} }
private GF256() { GF256Poly getZero() {
return zero;
}
GF256Poly getOne() {
return one;
}
/**
* @return the monomial representing coefficient * x^degree
*/
GF256Poly buildMonomial(int degree, int coefficient) {
if (degree < 0) {
throw new IllegalArgumentException();
}
if (coefficient == 0) {
return zero;
}
int[] coefficients = new int[degree + 1];
coefficients[0] = coefficient;
return new GF256Poly(this, coefficients);
} }
/** /**
@ -56,21 +90,21 @@ final class GF256 {
* *
* @return sum/difference of a and b * @return sum/difference of a and b
*/ */
static int addOrSubtract(int a, int b) { int addOrSubtract(int a, int b) {
return a ^ b; return a ^ b;
} }
/** /**
* @return 2 to the power of a in GF(256) * @return 2 to the power of a in GF(256)
*/ */
static int exp(int a) { int exp(int a) {
return exp[a]; return exp[a];
} }
/** /**
* @return base 2 log of a in GF(256) * @return base 2 log of a in GF(256)
*/ */
static int log(int a) { int log(int a) {
if (a == 0) { if (a == 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
@ -80,7 +114,7 @@ final class GF256 {
/** /**
* @return multiplicative inverse of a * @return multiplicative inverse of a
*/ */
static int inverse(int a) { int inverse(int a) {
if (a == 0) { if (a == 0) {
throw new ArithmeticException(); throw new ArithmeticException();
} }
@ -92,7 +126,7 @@ final class GF256 {
* @param b * @param b
* @return product of a and b in GF(256) * @return product of a and b in GF(256)
*/ */
static int multiply(int a, int b) { int multiply(int a, int b) {
if (a == 0 || b == 0) { if (a == 0 || b == 0) {
return 0; return 0;
} }

View file

@ -27,28 +27,23 @@ package com.google.zxing.common.reedsolomon;
*/ */
final class GF256Poly { final class GF256Poly {
/** private final GF256 field;
* Polynimal representing the monomial 0.
*/
static final GF256Poly ZERO = new GF256Poly(new int[]{0});
/**
* Polynimal representing the monomial 1.
*/
static final GF256Poly ONE = new GF256Poly(new int[]{1});
private final int[] coefficients; private final int[] coefficients;
/** /**
* @param field the {@link GF256} instance representing the field to use
* to perform computations
* @param coefficients coefficients as ints representing elements of GF(256), arranged * @param coefficients coefficients as ints representing elements of GF(256), arranged
* from most significant (highest-power term) coefficient to least significant * from most significant (highest-power term) coefficient to least significant
* @throws IllegalArgumentException if argument is null or empty, * @throws IllegalArgumentException if argument is null or empty,
* or if leading coefficient is 0 and this is not a * or if leading coefficient is 0 and this is not a
* constant polynomial (that is, it is not the monomial "0") * constant polynomial (that is, it is not the monomial "0")
*/ */
GF256Poly(int[] coefficients) { GF256Poly(GF256 field, int[] coefficients) {
if (coefficients == null || coefficients.length == 0) { if (coefficients == null || coefficients.length == 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
this.field = field;
if (coefficients.length > 1 && coefficients[0] == 0) { if (coefficients.length > 1 && coefficients[0] == 0) {
// Leading term must be non-zero for anything except the constant polynomial "0" // Leading term must be non-zero for anything except the constant polynomial "0"
int firstNonZero = 1; int firstNonZero = 1;
@ -56,7 +51,7 @@ final class GF256Poly {
firstNonZero++; firstNonZero++;
} }
if (firstNonZero == coefficients.length) { if (firstNonZero == coefficients.length) {
this.coefficients = ZERO.coefficients; this.coefficients = field.getZero().coefficients;
} else { } else {
this.coefficients = new int[coefficients.length - firstNonZero]; this.coefficients = new int[coefficients.length - firstNonZero];
System.arraycopy(coefficients, System.arraycopy(coefficients,
@ -84,21 +79,6 @@ final class GF256Poly {
return coefficients[0] == 0; return coefficients[0] == 0;
} }
/**
* @return the monomial representing coefficient * x^degree
*/
static GF256Poly buildMonomial(int degree, int coefficient) {
if (degree < 0) {
throw new IllegalArgumentException();
}
if (coefficient == 0) {
return ZERO;
}
int[] coefficients = new int[degree + 1];
coefficients[0] = coefficient;
return new GF256Poly(coefficients);
}
/** /**
* @return coefficient of x^degree term in this polynomial * @return coefficient of x^degree term in this polynomial
*/ */
@ -114,18 +94,18 @@ final class GF256Poly {
// Just return the x^0 coefficient // Just return the x^0 coefficient
return getCoefficient(0); return getCoefficient(0);
} }
final int size = coefficients.length; int size = coefficients.length;
if (a == 1) { if (a == 1) {
// Just the sum of the coefficients // Just the sum of the coefficients
int result = 0; int result = 0;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
result = GF256.addOrSubtract(result, coefficients[i]); result = field.addOrSubtract(result, coefficients[i]);
} }
return result; return result;
} }
int result = coefficients[0]; int result = coefficients[0];
for (int i = 1; i < size; i++) { for (int i = 1; i < size; i++) {
result = GF256.addOrSubtract(GF256.multiply(a, result), coefficients[i]); result = field.addOrSubtract(field.multiply(a, result), coefficients[i]);
} }
return result; return result;
} }
@ -139,15 +119,18 @@ final class GF256Poly {
int aToTheI = 1; int aToTheI = 1;
int sum = getCoefficient(1); int sum = getCoefficient(1);
int aSquared = GF256.multiply(a, a); int aSquared = field.multiply(a, a);
for (int i = 2; i < degree; i += 2) { for (int i = 2; i < degree; i += 2) {
aToTheI = GF256.multiply(aSquared, aToTheI); aToTheI = field.multiply(aSquared, aToTheI);
sum = GF256.addOrSubtract(sum, GF256.multiply(aToTheI, getCoefficient(i + 1))); sum = field.addOrSubtract(sum, field.multiply(aToTheI, getCoefficient(i + 1)));
} }
return sum; return sum;
} }
GF256Poly addOrSubtract(GF256Poly other) { GF256Poly addOrSubtract(GF256Poly other) {
if (!field.equals(other.field)) {
throw new IllegalArgumentException("GF256Polys do not have same GF256 field");
}
if (isZero()) { if (isZero()) {
return other; return other;
} }
@ -168,15 +151,18 @@ final class GF256Poly {
System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
for (int i = lengthDiff; i < largerCoefficients.length; i++) { for (int i = lengthDiff; i < largerCoefficients.length; i++) {
sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); sumDiff[i] = field.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
} }
return new GF256Poly(sumDiff); return new GF256Poly(field, sumDiff);
} }
GF256Poly multiply(GF256Poly other) { GF256Poly multiply(GF256Poly other) {
if (!field.equals(other.field)) {
throw new IllegalArgumentException("GF256Polys do not have same GF256 field");
}
if (isZero() || other.isZero()) { if (isZero() || other.isZero()) {
return ZERO; return field.getZero();
} }
int[] aCoefficients = this.coefficients; int[] aCoefficients = this.coefficients;
int aLength = aCoefficients.length; int aLength = aCoefficients.length;
@ -186,16 +172,16 @@ final class GF256Poly {
for (int i = 0; i < aLength; i++) { for (int i = 0; i < aLength; i++) {
int aCoeff = aCoefficients[i]; int aCoeff = aCoefficients[i];
for (int j = 0; j < bLength; j++) { for (int j = 0; j < bLength; j++) {
product[i + j] = GF256.addOrSubtract(product[i + j], product[i + j] = field.addOrSubtract(product[i + j],
GF256.multiply(aCoeff, bCoefficients[j])); field.multiply(aCoeff, bCoefficients[j]));
} }
} }
return new GF256Poly(product); return new GF256Poly(field, product);
} }
GF256Poly multiply(int scalar) { GF256Poly multiply(int scalar) {
if (scalar == 0) { if (scalar == 0) {
return ZERO; return field.getZero();
} }
if (scalar == 1) { if (scalar == 1) {
return this; return this;
@ -204,9 +190,9 @@ final class GF256Poly {
int[] product = new int[size]; int[] product = new int[size];
System.arraycopy(coefficients, 0, product, 0, size); System.arraycopy(coefficients, 0, product, 0, size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
product[i] = GF256.multiply(product[i], scalar); product[i] = field.multiply(product[i], scalar);
} }
return new GF256Poly(product); return new GF256Poly(field, product);
} }
GF256Poly multiplyByMonomial(int degree, int coefficient) { GF256Poly multiplyByMonomial(int degree, int coefficient) {
@ -214,15 +200,15 @@ final class GF256Poly {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (coefficient == 0) { if (coefficient == 0) {
return ZERO; return field.getZero();
} }
int size = coefficients.length; int size = coefficients.length;
int[] product = new int[size + degree]; int[] product = new int[size + degree];
System.arraycopy(coefficients, 0, product, 0, size); System.arraycopy(coefficients, 0, product, 0, size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
product[i] = GF256.multiply(product[i], coefficient); product[i] = field.multiply(product[i], coefficient);
} }
return new GF256Poly(product); return new GF256Poly(field, product);
} }
} }

View file

@ -41,7 +41,10 @@ import java.util.Vector;
*/ */
public final class ReedSolomonDecoder { public final class ReedSolomonDecoder {
private ReedSolomonDecoder() { private final GF256 field;
public ReedSolomonDecoder(GF256 field) {
this.field = field;
} }
/** /**
@ -53,26 +56,26 @@ public final class ReedSolomonDecoder {
* @param twoS number of error-correction codewords available * @param twoS number of error-correction codewords available
* @throws ReedSolomonException if decoding fails for any reaosn * @throws ReedSolomonException if decoding fails for any reaosn
*/ */
public static void decode(int[] received, int twoS) throws ReedSolomonException { public void decode(int[] received, int twoS) throws ReedSolomonException {
GF256Poly poly = new GF256Poly(received); GF256Poly poly = new GF256Poly(field, received);
int[] syndromeCoefficients = new int[twoS]; int[] syndromeCoefficients = new int[twoS];
for (int i = 0; i < twoS; i++) { for (int i = 0; i < twoS; i++) {
syndromeCoefficients[syndromeCoefficients.length - 1 - i] = poly.evaluateAt(GF256.exp(i)); syndromeCoefficients[syndromeCoefficients.length - 1 - i] = poly.evaluateAt(field.exp(i));
} }
GF256Poly syndrome = new GF256Poly(syndromeCoefficients); GF256Poly syndrome = new GF256Poly(field, syndromeCoefficients);
if (!syndrome.isZero()) { // Error if (!syndrome.isZero()) { // Error
GF256Poly[] sigmaOmega = GF256Poly[] sigmaOmega =
runEuclideanAlgorithm(GF256Poly.buildMonomial(twoS, 1), syndrome, twoS); runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS);
int[] errorLocations = findErrorLocations(sigmaOmega[0]); int[] errorLocations = findErrorLocations(sigmaOmega[0]);
int[] errorMagnitudes = findErrorMagnitudes(sigmaOmega[1], errorLocations); int[] errorMagnitudes = findErrorMagnitudes(sigmaOmega[1], errorLocations);
for (int i = 0; i < errorLocations.length; i++) { for (int i = 0; i < errorLocations.length; i++) {
int position = received.length - 1 - GF256.log(errorLocations[i]); int position = received.length - 1 - field.log(errorLocations[i]);
received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[i]); received[position] = field.addOrSubtract(received[position], errorMagnitudes[i]);
} }
} }
} }
private static GF256Poly[] runEuclideanAlgorithm(GF256Poly a, GF256Poly b, int R) private GF256Poly[] runEuclideanAlgorithm(GF256Poly a, GF256Poly b, int R)
throws ReedSolomonException { throws ReedSolomonException {
// Assume a's degree is >= b's // Assume a's degree is >= b's
if (a.getDegree() < b.getDegree()) { if (a.getDegree() < b.getDegree()) {
@ -83,10 +86,10 @@ public final class ReedSolomonDecoder {
GF256Poly rLast = a; GF256Poly rLast = a;
GF256Poly r = b; GF256Poly r = b;
GF256Poly sLast = GF256Poly.ONE; GF256Poly sLast = field.getOne();
GF256Poly s = GF256Poly.ZERO; GF256Poly s = field.getZero();
GF256Poly tLast = GF256Poly.ZERO; GF256Poly tLast = field.getZero();
GF256Poly t = GF256Poly.ONE; GF256Poly t = field.getOne();
// Run Euclidean algorithm until r's degree is less than R/2 // Run Euclidean algorithm until r's degree is less than R/2
while (r.getDegree() >= R / 2) { while (r.getDegree() >= R / 2) {
@ -103,13 +106,13 @@ public final class ReedSolomonDecoder {
throw new ReedSolomonException("r_{i-1} was zero"); throw new ReedSolomonException("r_{i-1} was zero");
} }
r = rLastLast; r = rLastLast;
GF256Poly q = GF256Poly.ZERO; GF256Poly q = field.getZero();
int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree()); int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
int dltInverse = GF256.inverse(denominatorLeadingTerm); int dltInverse = field.inverse(denominatorLeadingTerm);
while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
int degreeDiff = r.getDegree() - rLast.getDegree(); int degreeDiff = r.getDegree() - rLast.getDegree();
int scale = GF256.multiply(r.getCoefficient(r.getDegree()), dltInverse); int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);
q = q.addOrSubtract(GF256Poly.buildMonomial(degreeDiff, scale)); q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));
r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));
} }
@ -122,19 +125,19 @@ public final class ReedSolomonDecoder {
throw new ReedSolomonException("sigmaTilde(0) was zero"); throw new ReedSolomonException("sigmaTilde(0) was zero");
} }
int inverse = GF256.inverse(sigmaTildeAtZero); int inverse = field.inverse(sigmaTildeAtZero);
GF256Poly sigma = t.multiply(inverse); GF256Poly sigma = t.multiply(inverse);
GF256Poly omega = r.multiply(inverse); GF256Poly omega = r.multiply(inverse);
return new GF256Poly[]{sigma, omega}; return new GF256Poly[]{sigma, omega};
} }
private static int[] findErrorLocations(GF256Poly errorLocator) private int[] findErrorLocations(GF256Poly errorLocator)
throws ReedSolomonException { throws ReedSolomonException {
// This is a direct application of Chien's search // This is a direct application of Chien's search
Vector errorLocations = new Vector(3); Vector errorLocations = new Vector(3);
for (int i = 1; i < 256; i++) { for (int i = 1; i < 256; i++) {
if (errorLocator.evaluateAt(i) == 0) { if (errorLocator.evaluateAt(i) == 0) {
errorLocations.addElement(new Integer(GF256.inverse(i))); errorLocations.addElement(new Integer(field.inverse(i)));
} }
} }
if (errorLocations.size() != errorLocator.getDegree()) { if (errorLocations.size() != errorLocator.getDegree()) {
@ -147,22 +150,22 @@ public final class ReedSolomonDecoder {
return result; return result;
} }
private static int[] findErrorMagnitudes(GF256Poly errorEvaluator, private int[] findErrorMagnitudes(GF256Poly errorEvaluator,
int[] errorLocations) { int[] errorLocations) {
// This is directly applying Forney's Formula // This is directly applying Forney's Formula
int s = errorLocations.length; int s = errorLocations.length;
int[] result = new int[s]; int[] result = new int[s];
for (int i = 0; i < errorLocations.length; i++) { for (int i = 0; i < errorLocations.length; i++) {
int xiInverse = GF256.inverse(errorLocations[i]); int xiInverse = field.inverse(errorLocations[i]);
int denominator = 1; int denominator = 1;
for (int j = 0; j < s; j++) { for (int j = 0; j < s; j++) {
if (i != j) { if (i != j) {
denominator = GF256.multiply(denominator, denominator = field.multiply(denominator,
GF256.addOrSubtract(1, GF256.multiply(errorLocations[j], xiInverse))); field.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
} }
} }
result[i] = GF256.multiply(errorEvaluator.evaluateAt(xiInverse), result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),
GF256.inverse(denominator)); field.inverse(denominator));
} }
return result; return result;
} }

View file

@ -38,6 +38,8 @@ public final class QRCodeReader implements Reader {
private static final ResultPoint[] NO_POINTS = new ResultPoint[0]; private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
private final Decoder decoder = new Decoder();
/** /**
* Locates and decodes a QR code in an image. * Locates and decodes a QR code in an image.
* *
@ -54,11 +56,11 @@ public final class QRCodeReader implements Reader {
ResultPoint[] points; ResultPoint[] points;
if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) { if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
BitMatrix bits = extractPureBits(image); BitMatrix bits = extractPureBits(image);
text = Decoder.decode(bits); text = decoder.decode(bits);
points = NO_POINTS; points = NO_POINTS;
} else { } else {
DetectorResult result = new Detector(image).detect(); DetectorResult result = new Detector(image).detect();
text = Decoder.decode(result.getBits()); text = decoder.decode(result.getBits());
points = result.getPoints(); points = result.getPoints();
} }
return new Result(text, points); return new Result(text, points);

View file

@ -18,6 +18,7 @@ package com.google.zxing.qrcode.decoder;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.reedsolomon.GF256;
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
import com.google.zxing.common.reedsolomon.ReedSolomonException; import com.google.zxing.common.reedsolomon.ReedSolomonException;
@ -29,7 +30,10 @@ import com.google.zxing.common.reedsolomon.ReedSolomonException;
*/ */
public final class Decoder { public final class Decoder {
private Decoder() { private final ReedSolomonDecoder rsDecoder;
public Decoder() {
rsDecoder = new ReedSolomonDecoder(GF256.QR_CODE_FIELD);
} }
/** /**
@ -40,7 +44,7 @@ public final class Decoder {
* @return text encoded within the QR Code * @return text encoded within the QR Code
* @throws ReaderException if the QR Code cannot be decoded * @throws ReaderException if the QR Code cannot be decoded
*/ */
public static String decode(boolean[][] image) throws ReaderException { public String decode(boolean[][] image) throws ReaderException {
int dimension = image.length; int dimension = image.length;
BitMatrix bits = new BitMatrix(dimension); BitMatrix bits = new BitMatrix(dimension);
for (int i = 0; i < dimension; i++) { for (int i = 0; i < dimension; i++) {
@ -60,7 +64,7 @@ public final class Decoder {
* @return text encoded within the QR Code * @return text encoded within the QR Code
* @throws ReaderException if the QR Code cannot be decoded * @throws ReaderException if the QR Code cannot be decoded
*/ */
public static String decode(BitMatrix bits) throws ReaderException { public String decode(BitMatrix bits) throws ReaderException {
// Construct a parser and read version, error-correction level // Construct a parser and read version, error-correction level
BitMatrixParser parser = new BitMatrixParser(bits); BitMatrixParser parser = new BitMatrixParser(bits);
@ -103,7 +107,7 @@ public final class Decoder {
* @param numDataCodewords number of codewords that are data bytes * @param numDataCodewords number of codewords that are data bytes
* @throws ReaderException if error correction fails * @throws ReaderException if error correction fails
*/ */
private static void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ReaderException { private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ReaderException {
int numCodewords = codewordBytes.length; int numCodewords = codewordBytes.length;
// First read into an array of ints // First read into an array of ints
int[] codewordsInts = new int[numCodewords]; int[] codewordsInts = new int[numCodewords];
@ -112,7 +116,7 @@ public final class Decoder {
} }
int numECCodewords = codewordBytes.length - numDataCodewords; int numECCodewords = codewordBytes.length - numDataCodewords;
try { try {
ReedSolomonDecoder.decode(codewordsInts, numECCodewords); rsDecoder.decode(codewordsInts, numECCodewords);
} catch (ReedSolomonException rse) { } catch (ReedSolomonException rse) {
throw new ReaderException(rse.toString()); throw new ReaderException(rse.toString());
} }