From 7cae0d4cc98bc4943fb1cce54a799f5f4f3328a5 Mon Sep 17 00:00:00 2001 From: srowen Date: Fri, 14 Nov 2008 12:40:55 +0000 Subject: [PATCH] Added Reed-Solomon encoder, suitable for QR Code encoding git-svn-id: https://zxing.googlecode.com/svn/trunk@699 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../zxing/common/reedsolomon/GF256Poly.java | 86 +++++++++++++++---- .../reedsolomon/ReedSolomonEncoder.java | 64 ++++++++++++++ .../AbstractReedSolomonTestCase.java | 14 +++ .../ReedSolomonEncoderQRCodeTestCase.java | 39 +++++++++ 4 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java create mode 100644 core/test/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoderQRCodeTestCase.java diff --git a/core/src/com/google/zxing/common/reedsolomon/GF256Poly.java b/core/src/com/google/zxing/common/reedsolomon/GF256Poly.java index 022e2f777..8f9bcb344 100644 --- a/core/src/com/google/zxing/common/reedsolomon/GF256Poly.java +++ b/core/src/com/google/zxing/common/reedsolomon/GF256Poly.java @@ -66,6 +66,10 @@ final class GF256Poly { } } + int[] getCoefficients() { + return coefficients; + } + /** * @return degree of this polynomial */ @@ -111,25 +115,6 @@ final class GF256Poly { return result; } - /* - int evaluateFormatDerivativeAt(int a) { - int degree = getDegree(); - if (degree == 0) { - // Derivative of a constant is zero. - return 0; - } - - int aToTheI = 1; - int sum = getCoefficient(1); - int aSquared = field.multiply(a, a); - for (int i = 2; i < degree; i += 2) { - aToTheI = field.multiply(aSquared, aToTheI); - sum = field.addOrSubtract(sum, field.multiply(aToTheI, getCoefficient(i + 1))); - } - return sum; - } - */ - GF256Poly addOrSubtract(GF256Poly other) { if (!field.equals(other.field)) { throw new IllegalArgumentException("GF256Polys do not have same GF256 field"); @@ -212,4 +197,67 @@ final class GF256Poly { return new GF256Poly(field, product); } + GF256Poly[] divide(GF256Poly other) { + if (!field.equals(other.field)) { + throw new IllegalArgumentException("GF256Polys do not have same GF256 field"); + } + if (other.isZero()) { + throw new IllegalArgumentException("Divide by 0"); + } + + GF256Poly quotient = field.getZero(); + GF256Poly remainder = this; + + int denominatorLeadingTerm = other.getCoefficient(other.getDegree()); + int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm); + + while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { + int degreeDifference = remainder.getDegree() - other.getDegree(); + int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); + GF256Poly term = other.multiplyByMonomial(degreeDifference, scale); + GF256Poly iterationQuotient = field.buildMonomial(degreeDifference, scale); + quotient = quotient.addOrSubtract(iterationQuotient); + remainder = remainder.addOrSubtract(term); + } + + return new GF256Poly[] { quotient, remainder }; + } + + public String toString() { + StringBuffer result = new StringBuffer(8 * getDegree()); + for (int degree = getDegree(); degree >= 0; degree--) { + int coefficient = getCoefficient(degree); + if (coefficient != 0) { + if (coefficient < 0) { + result.append(" - "); + coefficient = -coefficient; + } else { + if (result.length() > 0) { + result.append(" + "); + } + } + if (degree == 0 || coefficient != 1) { + int alphaPower = field.log(coefficient); + if (alphaPower == 0) { + result.append('1'); + } else if (alphaPower == 1) { + result.append('a'); + } else { + result.append("a^"); + result.append(alphaPower); + } + } + if (degree != 0) { + if (degree == 1) { + result.append('x'); + } else { + result.append("x^"); + result.append(degree); + } + } + } + } + return result.toString(); + } + } diff --git a/core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java b/core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java new file mode 100644 index 000000000..1d5d37874 --- /dev/null +++ b/core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java @@ -0,0 +1,64 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.common.reedsolomon; + +import java.util.Vector; + +/** + *

Implements Reed-Solomon enbcoding, as the name implies.

+ * + * @author srowen@google.com (Sean Owen) + * @author William Rucklidge + */ +public final class ReedSolomonEncoder { + + private final GF256 field; + private final Vector cachedGenerators; + + public ReedSolomonEncoder(GF256 field) { + if (!GF256.QR_CODE_FIELD.equals(field)) { + throw new IllegalArgumentException("Only QR Code is supported at this time"); + } + this.field = field; + this.cachedGenerators = new Vector(); + cachedGenerators.addElement(new GF256Poly(field, new int[] { 1 })); + } + + private GF256Poly buildGenerator(int degree) { + if (degree >= cachedGenerators.size()) { + GF256Poly lastGenerator = (GF256Poly) cachedGenerators.elementAt(cachedGenerators.size() - 1); + for (int d = cachedGenerators.size(); d <= degree; d++) { + GF256Poly nextGenerator = lastGenerator.multiply(new GF256Poly(field, new int[] { 1, field.exp(d - 1) })); + cachedGenerators.addElement(nextGenerator); + lastGenerator = nextGenerator; + } + } + return (GF256Poly) cachedGenerators.elementAt(degree); + } + + public void encode(int[] toEncode, int ecBytes) { + int dataBytes = toEncode.length - ecBytes; + GF256Poly generator = buildGenerator(ecBytes); + int[] infoCoefficients = new int[dataBytes]; + System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes); + GF256Poly info = new GF256Poly(field, infoCoefficients); + info = info.multiplyByMonomial(ecBytes, 1); + GF256Poly remainder = info.divide(generator)[1]; + System.arraycopy(remainder.getCoefficients(), 0, toEncode, dataBytes, ecBytes); + } + +} diff --git a/core/test/src/com/google/zxing/common/reedsolomon/AbstractReedSolomonTestCase.java b/core/test/src/com/google/zxing/common/reedsolomon/AbstractReedSolomonTestCase.java index 6f7a615b4..7957f51b5 100644 --- a/core/test/src/com/google/zxing/common/reedsolomon/AbstractReedSolomonTestCase.java +++ b/core/test/src/com/google/zxing/common/reedsolomon/AbstractReedSolomonTestCase.java @@ -39,4 +39,18 @@ abstract class AbstractReedSolomonTestCase extends TestCase { } } + static void doTestQRCodeEncoding(int[] dataBytes, int[] expectedECBytes) { + int[] toEncode = new int[dataBytes.length + expectedECBytes.length]; + System.arraycopy(dataBytes, 0, toEncode, 0, dataBytes.length); + new ReedSolomonEncoder(GF256.QR_CODE_FIELD).encode(toEncode, expectedECBytes.length); + assertArraysEqual(dataBytes, 0, toEncode, 0, dataBytes.length); + assertArraysEqual(expectedECBytes, 0, toEncode, dataBytes.length, expectedECBytes.length); + } + + private static void assertArraysEqual(int[] expected, int expectedOffset, int[] actual, int actualOffset, int length) { + for (int i = 0; i < length; i++) { + assertEquals(expected[expectedOffset + i], actual[actualOffset + i]); + } + } + } \ No newline at end of file diff --git a/core/test/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoderQRCodeTestCase.java b/core/test/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoderQRCodeTestCase.java new file mode 100644 index 000000000..0cdba3f68 --- /dev/null +++ b/core/test/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoderQRCodeTestCase.java @@ -0,0 +1,39 @@ +/* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.common.reedsolomon; + +/** + * @author srowen@google.com (Sean Owen) + */ +public final class ReedSolomonEncoderQRCodeTestCase extends AbstractReedSolomonTestCase { + + /** + * Tests example given in ISO 18004, Annex I + */ + public void testISO18004Example() { + int[] dataBytes = new int[] { + 0x10, 0x20, 0x0C, 0x56, 0x61, 0x80, 0xEC, 0x11, + 0xEC, 0x11, 0xEC, 0x11, 0xEC, 0x11, 0xEC, 0x11 }; + int[] expectedECBytes = new int[] { + 0xA5, 0x24, 0xD4, 0xC1, 0xED, 0x36, 0xC7, 0x87, + 0x2C, 0x55 }; + doTestQRCodeEncoding(dataBytes, expectedECBytes); + } + + // Need more tests I am sure + +}