diff --git a/actionscript/core/src/com/google/zxing/common/reedsolomon/GenericGF.as b/actionscript/core/src/com/google/zxing/common/reedsolomon/GenericGF.as new file mode 100644 index 000000000..f764eb8ae --- /dev/null +++ b/actionscript/core/src/com/google/zxing/common/reedsolomon/GenericGF.as @@ -0,0 +1,199 @@ +/* + * Copyright 2007 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 com.google.zxing.common.flexdatatypes.IllegalArgumentException; + +/** + *

This class contains utility methods for performing mathematical operations over + * the Galois Fields. Operations use a given primitive polynomial in calculations.

+ * + *

Throughout this package, elements of the GF are represented as an int + * for convenience and speed (but at the cost of memory). + *

+ * + * @author Sean Owen + * @author David Olivier + */ +public class GenericGF +{ + + public static var AZTEC_DATA_12:GenericGF = new GenericGF(0x1069, 4096); // x^12 + x^6 + x^5 + x^3 + 1 + public static var AZTEC_DATA_10:GenericGF = new GenericGF(0x409, 1024); // x^10 + x^3 + 1 + public static var AZTEC_DATA_6:GenericGF = new GenericGF(0x43, 64); // x^6 + x + 1 + public static var AZTEC_PARAM:GenericGF = new GenericGF(0x13, 16); // x^4 + x + 1 + public static var QR_CODE_FIELD_256:GenericGF = new GenericGF(0x011D, 256); // x^8 + x^4 + x^3 + x^2 + 1 + public static var DATA_MATRIX_FIELD_256:GenericGF = new GenericGF(0x012D, 256); // x^8 + x^5 + x^3 + x^2 + 1 + public static var AZTEC_DATA_8:GenericGF = DATA_MATRIX_FIELD_256; + + private static var INITIALIZATION_THRESHOLD:int = 0; + + private var expTable:Array; + private var logTable:Array; + private var zero:GenericGFPoly; + private var one:GenericGFPoly; + public var size:int; + public var primitive:int; + private var initialized:Boolean = false; + + /** + * Create a representation of GF(size) 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 + */ + public function GenericGF(primitive:int, size:int) { + this.primitive = primitive; + this.size = size; + + if (size <= INITIALIZATION_THRESHOLD){ + initialize(); + } + } + + private function initialize():void{ + expTable = new Array(size); + logTable = new Array(size); + var x:int = 1; + for (var i:int = 0; i < size; i++) { + expTable[i] = x; + x <<= 1; // x = x * 2; we're assuming the generator alpha is 2 + if (x >= size) { + x ^= primitive; + x &= size-1; + } + } + for (var ii:int = 0; ii < size-1; ii++) { + logTable[expTable[ii]] = ii; + } + // logTable[0] == 0 but this should never be used + zero = new GenericGFPoly(this, [0]); + one = new GenericGFPoly(this, [1]); + initialized = true; + } + + private function checkInit():void{ + if (!initialized) { + initialize(); + } + } + + public function getZero():GenericGFPoly { + checkInit(); + + return zero; + } + + public function getOne():GenericGFPoly { + checkInit(); + + return one; + } + + /** + * @return the monomial representing coefficient * x^degree + */ + public function buildMonomial(degree:int, coefficient:int):GenericGFPoly { + checkInit(); + + if (degree < 0) { + throw new IllegalArgumentException(); + } + if (coefficient == 0) { + return zero; + } + var coefficients:Array = new Array(degree + 1); + coefficients[0] = coefficient; + return new GenericGFPoly(this, coefficients); + } + + /** + * Implements both addition and subtraction -- they are the same in GF(size). + * + * @return sum/difference of a and b + */ + public static function addOrSubtract(a:int, b:int):int { + return a ^ b; + } + + /** + * @return 2 to the power of a in GF(size) + */ + public function exp(a:int):int { + checkInit(); + + return expTable[a]; + } + + /** + * @return base 2 log of a in GF(size) + */ + public function log(a:int):int { + checkInit(); + + if (a == 0) { + throw new IllegalArgumentException(); + } + return logTable[a]; + } + + /** + * @return multiplicative inverse of a + */ + public function inverse(a:int):int { + checkInit(); + + if (a == 0) { + throw new Error("arithmetic exception"); + } + return expTable[size - logTable[a] - 1]; + } + + /** + * @param a + * @param b + * @return product of a and b in GF(size) + */ + public function multiply(a:int, b:int):int { + checkInit(); + + if (a == 0 || b == 0) { + return 0; + } + + if (a<0 || b <0 || a>=size || b >=size){ + a++; + } + + var logSum:int = logTable[a] + logTable[b]; + var result:int = expTable[(logSum % size) + Math.floor(logSum / size)]; + return result; + } + + public function getSize():int{ + return size; + } + + public function Equals(other:GenericGF):Boolean + { + return ((this.primitive == other.primitive) && (this.size == other.size)); + } + +} +} \ No newline at end of file diff --git a/actionscript/core/src/com/google/zxing/common/reedsolomon/GenericGFPoly.as b/actionscript/core/src/com/google/zxing/common/reedsolomon/GenericGFPoly.as new file mode 100644 index 000000000..352bb4d15 --- /dev/null +++ b/actionscript/core/src/com/google/zxing/common/reedsolomon/GenericGFPoly.as @@ -0,0 +1,295 @@ +/* + * Copyright 2007 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 com.google.zxing.common.flexdatatypes.IllegalArgumentException; +import com.google.zxing.common.flexdatatypes.StringBuilder; +import com.google.zxing.common.flexdatatypes.Utils; +/** + *

Represents a polynomial whose coefficients are elements of a GF. + * Instances of this class are immutable.

+ * + *

Much credit is due to William Rucklidge since portions of this code are an indirect + * port of his C++ Reed-Solomon implementation.

+ * + * @author Sean Owen + */ +public class GenericGFPoly { + + public var field:GenericGF; + public var coefficients:Array; + + /** + * @param field the {@link GenericGF} instance representing the field to use + * to perform computations + * @param coefficients coefficients as ints representing elements of GF(size), arranged + * from most significant (highest-power term) coefficient to least significant + * @throws IllegalArgumentException if argument is null or empty, + * or if leading coefficient is 0 and this is not a + * constant polynomial (that is, it is not the monomial "0") + */ + public function GenericGFPoly(field:GenericGF, coefficients:Array) { + if (coefficients == null || coefficients.length == 0) { + throw new IllegalArgumentException(); + } + this.field = field; + var coefficientsLength:int = coefficients.length; + if (coefficientsLength > 1 && coefficients[0] == 0) { + // Leading term must be non-zero for anything except the constant polynomial "0" + var firstNonZero:int = 1; + while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) { + firstNonZero++; + } + if (firstNonZero == coefficientsLength) { + this.coefficients = field.getZero().coefficients; + } else { + this.coefficients = new Array(coefficientsLength - firstNonZero); + Utils.arraycopy(coefficients,firstNonZero,this.coefficients,0,this.coefficients.length); + } + } else + { + this.coefficients = coefficients; + } + } + + public function getCoefficients():Array { + return coefficients; + } + + /** + * @return degree of this polynomial + */ + public function getDegree():int { + return coefficients.length - 1; + } + + /** + * @return true iff this polynomial is the monomial "0" + */ + public function isZero():Boolean { + return coefficients[0] == 0; + } + + /** + * @return coefficient of x^degree term in this polynomial + */ + public function getCoefficient(degree:int):int + { + return coefficients[coefficients.length - 1 - degree]; + } + + /** + * @return evaluation of this polynomial at a given point + */ + public function evaluateAt(a:int):int { + if (a == 0) { + // Just return the x^0 coefficient + return getCoefficient(0); + } + var size:int = coefficients.length; + if (a == 1) { + // Just the sum of the coefficients + var result:int = 0; + for (var i:int = 0; i < size; i++) { + result = GenericGF.addOrSubtract(result, coefficients[i]); + } + return result; + } + result = coefficients[0]; + for (i = 1; i < size; i++) { + result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]); + } + return result; + } + + public function addOrSubtract(other:GenericGFPoly):GenericGFPoly { + if (!field.Equals(other.field)) { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (isZero()) { + return other; + } + if (other.isZero()) { + return this; + } + + var smallerCoefficients:Array = this.coefficients; + var largerCoefficients:Array = other.coefficients; + if (smallerCoefficients.length > largerCoefficients.length) { + var temp:Array = smallerCoefficients; + smallerCoefficients = largerCoefficients; + largerCoefficients = temp; + } + var sumDiff:Array = new Array(largerCoefficients.length); + var lengthDiff:int = largerCoefficients.length - smallerCoefficients.length; + // Copy high-order terms only found in higher-degree polynomial's coefficients + Utils.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); + + for (var i:int = lengthDiff; i < largerCoefficients.length; i++) { + sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); + } + + return new GenericGFPoly(field, sumDiff); + } + + public function multiply(other:*):GenericGFPoly + { + if (other is int) { return this.multiply_scalar(other as int);} + other = (other as GenericGFPoly); + if (!field.Equals(other.field)) { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (isZero() || other.isZero()) { + return field.getZero(); + } + var aCoefficients:Array = this.coefficients; + var aLength:int = aCoefficients.length; + var bCoefficients:Array = other.coefficients; + var bLength:int = bCoefficients.length; + var product:Array = new Array(aLength + bLength - 1); + for (var i:int = 0; i < aLength; i++) { + var aCoeff:int = aCoefficients[i]; + for (var j:int = 0; j < bLength; j++) { + product[i + j] = GenericGF.addOrSubtract(product[i + j], + field.multiply(aCoeff, bCoefficients[j])); + } + } + return new GenericGFPoly(field, product); + } + + public function multiply_scalar(scalar:int):GenericGFPoly { + if (scalar == 0) { + return field.getZero(); + } + if (scalar == 1) { + return this; + } + var size:int = coefficients.length; + var product:Array = new Array(size); + for (var i:int = 0; i < size; i++) { + product[i] = field.multiply(coefficients[i], scalar); + } + return new GenericGFPoly(field, product); + } + + public function multiplyByMonomial(degree:int, coefficient:int):GenericGFPoly { + if (degree < 0) { + throw new IllegalArgumentException(); + } + if (coefficient == 0) { + return field.getZero(); + } + var size:int = coefficients.length; + var product:Array = new Array(size + degree); + for (var i:int = 0; i < size; i++) + { + product[i] = field.multiply(coefficients[i], coefficient); + } + return new GenericGFPoly(field, product); + } + + public function divide(other:GenericGFPoly):Array + { + if (!field.Equals(other.field)) + { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (other.isZero()) + { + throw new IllegalArgumentException("Divide by 0"); + } + + var quotient:GenericGFPoly = field.getZero(); + var remainder:GenericGFPoly = this; + + var denominatorLeadingTerm:int = other.getCoefficient(other.getDegree()); + var inverseDenominatorLeadingTerm:int = field.inverse(denominatorLeadingTerm); + + while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { + var degreeDifference:int = remainder.getDegree() - other.getDegree(); + var scale:int = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); + var term:GenericGFPoly = other.multiplyByMonomial(degreeDifference, scale); + var iterationQuotient:GenericGFPoly = field.buildMonomial(degreeDifference, scale); + quotient = quotient.addOrSubtract(iterationQuotient); + remainder = remainder.addOrSubtract(term); + } + + return [ quotient, remainder ]; + } + + public function toString():String + { + var result:StringBuilder = new StringBuilder(8 * getDegree()); + for (var degree:int = getDegree(); degree >= 0; degree--) { + var coefficient:int = getCoefficient(degree); + if (coefficient != 0) { + if (coefficient < 0) { + result.Append(" - "); + coefficient = -coefficient; + } else { + if (result.length > 0) { + result.Append(" + "); + } + } + if (degree == 0 || coefficient != 1) { + var alphaPower:int = 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(); + } + + public function Equals(other:GenericGFPoly):Boolean + { + + if (this.field == other.field) + { + if (this.coefficients.length == other.coefficients.length) + { + for (var i:int=0;iImplements Reed-Solomon decoding, as the name implies.

- * - *

The algorithm will not be explained here, but the following references were helpful - * in creating this implementation:

- * - * - * - *

Much credit is due to William Rucklidge since portions of this code are an indirect - * port of his C++ Reed-Solomon implementation.

- * - * @author Sean Owen - * @author William Rucklidge - * @author sanfordsquires - */ - public class ReedSolomonDecoder - { - private var field:GF256; - - public function ReedSolomonDecoder(field:GF256) { - this.field = field; - } - - /** - *

Decodes given set of received codewords, which include both data and error-correction - * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place, - * in the input.

- * - * @param received data and error-correction codewords - * @param twoS number of error-correction codewords available - * @throws ReedSolomonException if decoding fails for any reason - */ - public function decode(received:Array, twoS:int):void { - var poly:GF256Poly = new GF256Poly(field, received); - var syndromeCoefficients:Array = new Array(twoS); - var dataMatrix:Boolean = (field == GF256.DATA_MATRIX_FIELD); - var noError:Boolean = true; - for (var i:int = 0; i < twoS; i++) { - // Thanks to sanfordsquires for this fix: - var eval:int = poly.evaluateAt(field.exp(dataMatrix ? i + 1 : i)); - syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval; - if (eval != 0) { - noError = false; - } - } - if (noError) { - return; - } - var syndrome:GF256Poly = new GF256Poly(field, syndromeCoefficients); - var sigmaOmega:Array = runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS); - var sigma:GF256Poly = sigmaOmega[0]; - var omega:GF256Poly = sigmaOmega[1]; - var errorLocations:Array = findErrorLocations(sigma); - var errorMagnitudes:Array = findErrorMagnitudes(omega, errorLocations, dataMatrix); - for (var j:int = 0; j < errorLocations.length; j++) { - var position:int = received.length - 1 - field.log(errorLocations[j]); - if (position < 0) { - throw new ReedSolomonException("Bad error location"); - } - received[position] = GF256.addOrSubtract(received[position], errorMagnitudes[j]); - } - } - - private function runEuclideanAlgorithm(a:GF256Poly, b:GF256Poly, R:int ):Array - { - // Assume a's degree is >= b's - if (a.getDegree() < b.getDegree()) { - var temp:GF256Poly = a; - a = b; - b = temp; - } - - var rLast:GF256Poly = a; - var r:GF256Poly = b; - var sLast:GF256Poly = field.getOne(); - var s:GF256Poly = field.getZero(); - var tLast:GF256Poly = field.getZero(); - var t:GF256Poly = field.getOne(); - - // Run Euclidean algorithm until r's degree is less than R/2 - while (r.getDegree() >= R / 2) { - var rLastLast:GF256Poly = rLast; - var sLastLast:GF256Poly = sLast; - var tLastLast:GF256Poly = tLast; - rLast = r; - sLast = s; - tLast = t; - - // Divide rLastLast by rLast, with quotient in q and remainder in r - if (rLast.isZero()) { - // Oops, Euclidean algorithm already terminated? - throw new ReedSolomonException("r_{i-1} was zero"); - } - r = rLastLast; - var q:GF256Poly = field.getZero(); - var denominatorLeadingTerm:int = rLast.getCoefficient(rLast.getDegree()); - var dltInverse:int = field.inverse(denominatorLeadingTerm); - while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { - var degreeDiff:int = r.getDegree() - rLast.getDegree(); - var scale:int = field.multiply(r.getCoefficient(r.getDegree()), dltInverse); - q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale)); - r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); - } - - s = q.multiply(sLast).addOrSubtract(sLastLast); - t = q.multiply(tLast).addOrSubtract(tLastLast); - } - - var sigmaTildeAtZero:int = t.getCoefficient(0); - if (sigmaTildeAtZero == 0) { - throw new ReedSolomonException("sigmaTilde(0) was zero"); - } - - var inverse:int = field.inverse(sigmaTildeAtZero); - var sigma:GF256Poly = t.multiply(inverse); - var omega:GF256Poly = r.multiply(inverse); - return [sigma, omega]; - } - - private function findErrorLocations(errorLocator:GF256Poly):Array{ - // This is a direct application of Chien's search - var numErrors:int = errorLocator.getDegree(); - if (numErrors == 1) { // shortcut - return [errorLocator.getCoefficient(1)]; - } - var result:Array = new Array(numErrors); - var e:int = 0; - for (var i:int = 1; i < 256 && e < numErrors; i++) { - if (errorLocator.evaluateAt(i) == 0) { - result[e] = field.inverse(i); - e++; - } - } - if (e != numErrors) { - throw new ReedSolomonException("Error locator degree does not match number of roots"); - } - return result; - } - - private function findErrorMagnitudes(errorEvaluator:GF256Poly , errorLocations:Array, dataMatrix:Boolean):Array { - // This is directly applying Forney's Formula - var s:int = errorLocations.length; - var result:Array = new Array(s); - for (var i:int = 0; i < s; i++) { - var xiInverse:int = field.inverse(errorLocations[i]); - var denominator:int = 1; - for (var j:int = 0; j < s; j++) { - if (i != j) { - denominator = field.multiply(denominator, - GF256.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse))); - } - } - result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), - field.inverse(denominator)); - // Thanks to sanfordsquires for this fix: - if (dataMatrix) { - result[i] = field.multiply(result[i], xiInverse); - } - } - return result; - } - - } - +/* + * Copyright 2007 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 +{ + +/** + *

Implements Reed-Solomon decoding, as the name implies.

+ * + *

The algorithm will not be explained here, but the following references were helpful + * in creating this implementation:

+ * + * + * + *

Much credit is due to William Rucklidge since portions of this code are an indirect + * port of his C++ Reed-Solomon implementation.

+ * + * @author Sean Owen + * @author William Rucklidge + * @author sanfordsquires + */ +public class ReedSolomonDecoder { + + private var field:GenericGF; + + public function ReedSolomonDecoder(field:GenericGF) + { + this.field = field; + } + + /** + *

Decodes given set of received codewords, which include both data and error-correction + * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place, + * in the input.

+ * + * @param received data and error-correction codewords + * @param twoS number of error-correction codewords available + * @throws ReedSolomonException if decoding fails for any reason + */ + public function decode(received:Array, twoS:int):void + { + /* debug */ + /*received = [66,102,135,71,71,3,162,242,246,118,246,246,118,198,82,230,54,246,210,246,119,119,66,246,227, + 247,83,214,38,199,86,86,230,150,198,82,230,54,246,208,236,17,236,17,236,17,236,17,236,17, + 236,17,236,17,236,69,165,146,99,159,55,25,86,244,208,192,209,50,8,174]; + twoS = 15; + */ + /* debug */ + + var poly:GenericGFPoly = new GenericGFPoly(field, received); + var syndromeCoefficients:Array = new Array(twoS); + var dataMatrix:Boolean = field.Equals(GenericGF.DATA_MATRIX_FIELD_256); + var noError:Boolean = true; + for (var i:int = 0; i < twoS; i++) { + // Thanks to sanfordsquires for this fix: + var eval:int = poly.evaluateAt(field.exp(dataMatrix ? i + 1 : i)); + syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval; + if (eval != 0) { + noError = false; + } + } + if (noError) { + return; + } + var syndrome:GenericGFPoly = new GenericGFPoly(field, syndromeCoefficients); + var sigmaOmega:Array = runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS); + var sigma:GenericGFPoly = sigmaOmega[0]; + var omega:GenericGFPoly = sigmaOmega[1]; + var errorLocations:Array = findErrorLocations(sigma); + var errorMagnitudes:Array = findErrorMagnitudes(omega, errorLocations, dataMatrix); + for (i = 0; i < errorLocations.length; i++) { + var position:int = received.length - 1 - field.log(errorLocations[i]); + if (position < 0) { + throw new ReedSolomonException("Bad error location"); + } + received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]); + } + } + + private function runEuclideanAlgorithm(a:GenericGFPoly, b:GenericGFPoly, R:int):Array + { + // Assume a's degree is >= b's + if (a.getDegree() < b.getDegree()) { + var temp:GenericGFPoly = a; + a = b; + b = temp; + } + + var rLast:GenericGFPoly = a; + var r:GenericGFPoly = b; + var sLast:GenericGFPoly = field.getOne(); + var s:GenericGFPoly = field.getZero(); + var tLast:GenericGFPoly = field.getZero(); + var t:GenericGFPoly = field.getOne(); + + // Run Euclidean algorithm until r's degree is less than R/2 + while (r.getDegree() >= R / 2) { + var rLastLast:GenericGFPoly = rLast; + var sLastLast:GenericGFPoly = sLast; + var tLastLast:GenericGFPoly = tLast; + rLast = r; + sLast = s; + tLast = t; + + // Divide rLastLast by rLast, with quotient in q and remainder in r + if (rLast.isZero()) + { + // Oops, Euclidean algorithm already terminated? + throw new ReedSolomonException("r_{i-1} was zero"); + } + r = rLastLast; + var q:GenericGFPoly = field.getZero(); + var denominatorLeadingTerm:int = rLast.getCoefficient(rLast.getDegree()); + var dltInverse:int = field.inverse(denominatorLeadingTerm); + while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { + var degreeDiff:int = r.getDegree() - rLast.getDegree(); + var scale:int = field.multiply(r.getCoefficient(r.getDegree()), dltInverse); + q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale)); + r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); + } + + s = q.multiply(sLast).addOrSubtract(sLastLast); + t = q.multiply(tLast).addOrSubtract(tLastLast); + } + + var sigmaTildeAtZero:int = t.getCoefficient(0); + if (sigmaTildeAtZero == 0) { + throw new ReedSolomonException("sigmaTilde(0) was zero"); + } + + var inverse:int = field.inverse(sigmaTildeAtZero); + var sigma:GenericGFPoly = t.multiply(inverse); + var omega:GenericGFPoly = r.multiply(inverse); + return [sigma, omega]; + } + + private function findErrorLocations(errorLocator:GenericGFPoly ) :Array { + // This is a direct application of Chien's search + var numErrors:int = errorLocator.getDegree(); + if (numErrors == 1) + { // shortcut + return [errorLocator.getCoefficient(1) ]; + } + var result:Array = new Array(numErrors); + var e:int = 0; + for (var i:int = 1; i < field.getSize() && e < numErrors; i++) + { + if (errorLocator.evaluateAt(i) == 0) + { + result[e] = field.inverse(i); + e++; + } + } + if (e != numErrors) { + throw new ReedSolomonException("Error locator degree does not match number of roots"); + } + return result; + } + + private function findErrorMagnitudes(errorEvaluator:GenericGFPoly, errorLocations:Array, dataMatrix:Boolean):Array { + // This is directly applying Forney's Formula + var s:int = errorLocations.length; + var result:Array = new Array(s); + for (var i:int = 0; i < s; i++) { + var xiInverse:int = field.inverse(errorLocations[i]); + var denominator:int = 1; + for (var j:int = 0; j < s; j++) { + if (i != j) + { + //denominator = field.multiply(denominator, GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse))); + // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug. + // Below is a funny-looking workaround from Steven Parkes + var term:int = field.multiply(errorLocations[j], xiInverse); + var termPlus1:int = (term & 0x1) == 0 ? term | 1 : term & ~1; + denominator = field.multiply(denominator, termPlus1); + } + } + result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse),field.inverse(denominator)); + // Thanks to sanfordsquires for this fix: + if (dataMatrix) + { + result[i] = field.multiply(result[i], xiInverse); + } + } + return result; + } + +} } \ No newline at end of file diff --git a/actionscript/core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.as b/actionscript/core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.as index 2dacf750c..100c786f3 100644 --- a/actionscript/core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.as +++ b/actionscript/core/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.as @@ -1,81 +1,83 @@ -/* - * 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 -{ - public class ReedSolomonEncoder - { - import com.google.zxing.common.flexdatatypes.ArrayList; - import com.google.zxing.common.flexdatatypes.IllegalArgumentException; - - private var Field:GF256 ; - private var cachedGenerators:ArrayList; - - public function ReedSolomonEncoder(field:GF256 ) { - if (GF256.QR_CODE_FIELD != field) { - throw new IllegalArgumentException("Only QR Code is supported at this time"); - } - this.Field = field; - this.cachedGenerators = new ArrayList(); - cachedGenerators.Add(new GF256Poly(field, [ 1 ])); - } - - private function buildGenerator(degree:int):GF256Poly { - if (degree >= cachedGenerators.Count) { - var lastGenerator:GF256Poly = cachedGenerators.getObjectByIndex(cachedGenerators.Count - 1) as GF256Poly; - for (var d:int = cachedGenerators.Count; d <= degree; d++) - { - var nextGenerator:GF256Poly = lastGenerator.multiply(new GF256Poly(Field, [ 1, Field.exp(d - 1)])); - cachedGenerators.Add(nextGenerator); - lastGenerator = nextGenerator; - } - } - return (cachedGenerators.getObjectByIndex(degree) as GF256Poly); - } - - public function encode( toEncode:Array, ecBytes:int) :void{ - if (ecBytes == 0) { - throw new IllegalArgumentException("No error correction bytes"); - } - var dataBytes:int = toEncode.length - ecBytes; - if (dataBytes <= 0) { - throw new IllegalArgumentException("No data bytes provided"); - } - var generator:GF256Poly = buildGenerator(ecBytes); - var infoCoefficients:Array = new Array(dataBytes); - //System.Array.Copy(toEncode, 0, infoCoefficients, 0, dataBytes); - for (var ii:int=0;iiImplements Reed-Solomon enbcoding, as the name implies.

+ * + * @author Sean Owen + * @author William Rucklidge + */ +public class ReedSolomonEncoder +{ + + private var field:GenericGF; + private var cachedGenerators:ArrayList; + + public function ReedSolomonEncoder(field:GenericGF ) { + if (!GenericGF.QR_CODE_FIELD_256.Equals(field)) { + throw new IllegalArgumentException("Only QR Code is supported at this time"); + } + this.field = field; + this.cachedGenerators = new ArrayList(); + cachedGenerators.addElement(new GenericGFPoly(field, [ 1 ])); + } + + private function buildGenerator(degree:int):GenericGFPoly { + if (degree >= cachedGenerators.size()) { + var lastGenerator:GenericGFPoly= (cachedGenerators.elementAt(cachedGenerators.size() - 1) as GenericGFPoly); + for (var d:int = cachedGenerators.size(); d <= degree; d++) { + var nextGenerator:GenericGFPoly = lastGenerator.multiply(new GenericGFPoly(field, [ 1, field.exp(d - 1) ])); + cachedGenerators.addElement(nextGenerator); + lastGenerator = nextGenerator; + } + } + return (cachedGenerators.elementAt(degree) as GenericGFPoly) + } + + public function encode(toEncode:Array, ecBytes:int):void + { + if (ecBytes == 0) + { + throw new IllegalArgumentException("No error correction bytes"); + } + var dataBytes:int = toEncode.length - ecBytes; + if (dataBytes <= 0) + { + throw new IllegalArgumentException("No data bytes provided"); + } + var generator:GenericGFPoly = buildGenerator(ecBytes); + var infoCoefficients:Array = new Array(dataBytes); + Utils.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes); + var info:GenericGFPoly = new GenericGFPoly(field, infoCoefficients); + info = info.multiplyByMonomial(ecBytes, 1); + var remainder:GenericGFPoly = info.divide(generator)[1]; + var coefficients:Array = remainder.getCoefficients(); + var numZeroCoefficients:int = ecBytes - coefficients.length; + for (var i:int = 0; i < numZeroCoefficients; i++) + { + toEncode[dataBytes + i] = 0; + } + Utils.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length); + } + +} } \ No newline at end of file diff --git a/actionscript/core/src/com/google/zxing/common/reedsolomon/ReedSolomonException.as b/actionscript/core/src/com/google/zxing/common/reedsolomon/ReedSolomonException.as index 4e39dba83..b5cbbe7fd 100644 --- a/actionscript/core/src/com/google/zxing/common/reedsolomon/ReedSolomonException.as +++ b/actionscript/core/src/com/google/zxing/common/reedsolomon/ReedSolomonException.as @@ -1,11 +1,34 @@ -package com.google.zxing.common.reedsolomon -{ - public class ReedSolomonException extends Error - { - public function ReedSolomonException(message:String="") - { - super(message); - } - } - +/* + * Copyright 2007 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 +{ + + /** + *

Thrown when an exception occurs during Reed-Solomon decoding, such as when + * there are too many errors to correct.

+ * + * @author Sean Owen + */ + public class ReedSolomonException extends Error + { + public function ReedSolomonException(message:String) + { + super(message); + } + + } } \ No newline at end of file