Added Reed-Solomon encoder, suitable for QR Code encoding
authorsrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Fri, 14 Nov 2008 12:40:55 +0000 (12:40 +0000)
committersrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Fri, 14 Nov 2008 12:40:55 +0000 (12:40 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@699 59b500cc-1b3d-0410-9834-0bbf25fbcc57

core/src/com/google/zxing/common/reedsolomon/GF256Poly.java
core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java [new file with mode: 0644]
core/test/src/com/google/zxing/common/reedsolomon/AbstractReedSolomonTestCase.java
core/test/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoderQRCodeTestCase.java [new file with mode: 0644]

index 022e2f7..8f9bcb3 100644 (file)
@@ -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 (file)
index 0000000..1d5d378
--- /dev/null
@@ -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;
+
+/**
+ * <p>Implements Reed-Solomon enbcoding, as the name implies.</p>
+ *
+ * @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);
+  }
+
+}
index 6f7a615..7957f51 100644 (file)
@@ -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 (file)
index 0000000..0cdba3f
--- /dev/null
@@ -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
+
+}