mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 12:22:34 +00:00
pdf417 ec + test
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 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.pdf417.decoder.ec;
|
||||
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonTestCase;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
abstract class AbstractErrorCorrectionTestCase extends Assert {
|
||||
|
||||
static void corrupt(int[] received, int howMany, Random random) {
|
||||
ReedSolomonTestCase.corrupt(received, howMany, random, 929);
|
||||
}
|
||||
|
||||
static int[] erase(int[] received, int howMany, Random random) {
|
||||
BitSet erased = new BitSet(received.length);
|
||||
int[] erasures = new int[howMany];
|
||||
int erasureOffset = 0;
|
||||
for (int j = 0; j < howMany; j++) {
|
||||
int location = random.nextInt(received.length);
|
||||
if (erased.get(location)) {
|
||||
j--;
|
||||
} else {
|
||||
erased.set(location);
|
||||
received[location] = 0;
|
||||
erasures[erasureOffset++] = location;
|
||||
}
|
||||
}
|
||||
return erasures;
|
||||
}
|
||||
|
||||
static Random getRandom() {
|
||||
return new Random(0xDEADBEEF);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 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.pdf417.decoder.ec;
|
||||
|
||||
import com.google.zxing.ChecksumException;
|
||||
|
||||
/**
|
||||
* <p>PDF417 error correction implementation.</p>
|
||||
*
|
||||
* <p>This <a href="http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction#Example">example</a>
|
||||
* is quite useful in understanding the algorithm.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see com.google.zxing.common.reedsolomon.ReedSolomonDecoder
|
||||
*/
|
||||
public final class ErrorCorrection {
|
||||
|
||||
private final ModulusGF field;
|
||||
|
||||
public ErrorCorrection() {
|
||||
this.field = ModulusGF.PDF417_GF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param received received codewords
|
||||
* @param numECCodewords number of those codewords used for EC
|
||||
* @param erasures location of erasures
|
||||
* @return number of errors
|
||||
* @throws ChecksumException if errors cannot be corrected, maybe because of too many errors
|
||||
*/
|
||||
public int decode(int[] received,
|
||||
int numECCodewords,
|
||||
int[] erasures) throws ChecksumException {
|
||||
|
||||
ModulusPoly poly = new ModulusPoly(field, received);
|
||||
int[] S = new int[numECCodewords];
|
||||
boolean error = false;
|
||||
for (int i = numECCodewords; i > 0; i--) {
|
||||
int eval = poly.evaluateAt(field.exp(i));
|
||||
S[numECCodewords - i] = eval;
|
||||
if (eval != 0) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ModulusPoly knownErrors = field.getOne();
|
||||
if (erasures != null) {
|
||||
for (int erasure : erasures) {
|
||||
int b = field.exp(received.length - 1 - erasure);
|
||||
// Add (1 - bx) term:
|
||||
ModulusPoly term = new ModulusPoly(field, new int[]{field.subtract(0, b), 1});
|
||||
knownErrors = knownErrors.multiply(term);
|
||||
}
|
||||
}
|
||||
|
||||
ModulusPoly syndrome = new ModulusPoly(field, S);
|
||||
//syndrome = syndrome.multiply(knownErrors);
|
||||
|
||||
ModulusPoly[] sigmaOmega =
|
||||
runEuclideanAlgorithm(field.buildMonomial(numECCodewords, 1), syndrome, numECCodewords);
|
||||
ModulusPoly sigma = sigmaOmega[0];
|
||||
ModulusPoly omega = sigmaOmega[1];
|
||||
|
||||
//sigma = sigma.multiply(knownErrors);
|
||||
|
||||
int[] errorLocations = findErrorLocations(sigma);
|
||||
int[] errorMagnitudes = findErrorMagnitudes(omega, sigma, errorLocations);
|
||||
|
||||
for (int i = 0; i < errorLocations.length; i++) {
|
||||
int position = received.length - 1 - field.log(errorLocations[i]);
|
||||
if (position < 0) {
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
received[position] = field.subtract(received[position], errorMagnitudes[i]);
|
||||
}
|
||||
return errorLocations.length;
|
||||
}
|
||||
|
||||
private ModulusPoly[] runEuclideanAlgorithm(ModulusPoly a, ModulusPoly b, int R)
|
||||
throws ChecksumException {
|
||||
// Assume a's degree is >= b's
|
||||
if (a.getDegree() < b.getDegree()) {
|
||||
ModulusPoly temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
ModulusPoly rLast = a;
|
||||
ModulusPoly r = b;
|
||||
ModulusPoly tLast = field.getZero();
|
||||
ModulusPoly t = field.getOne();
|
||||
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while (r.getDegree() >= R / 2) {
|
||||
ModulusPoly rLastLast = rLast;
|
||||
ModulusPoly tLastLast = tLast;
|
||||
rLast = r;
|
||||
tLast = t;
|
||||
|
||||
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||
if (rLast.isZero()) {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
r = rLastLast;
|
||||
ModulusPoly q = field.getZero();
|
||||
int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
|
||||
int dltInverse = field.inverse(denominatorLeadingTerm);
|
||||
while (r.getDegree() >= rLast.getDegree() && !r.isZero()) {
|
||||
int degreeDiff = r.getDegree() - rLast.getDegree();
|
||||
int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse);
|
||||
q = q.add(field.buildMonomial(degreeDiff, scale));
|
||||
r = r.subtract(rLast.multiplyByMonomial(degreeDiff, scale));
|
||||
}
|
||||
|
||||
t = q.multiply(tLast).subtract(tLastLast).negative();
|
||||
}
|
||||
|
||||
int sigmaTildeAtZero = t.getCoefficient(0);
|
||||
if (sigmaTildeAtZero == 0) {
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
|
||||
int inverse = field.inverse(sigmaTildeAtZero);
|
||||
ModulusPoly sigma = t.multiply(inverse);
|
||||
ModulusPoly omega = r.multiply(inverse);
|
||||
return new ModulusPoly[]{sigma, omega};
|
||||
}
|
||||
|
||||
private int[] findErrorLocations(ModulusPoly errorLocator) throws ChecksumException {
|
||||
// This is a direct application of Chien's search
|
||||
int numErrors = errorLocator.getDegree();
|
||||
int[] result = new int[numErrors];
|
||||
int e = 0;
|
||||
for (int i = 1; i < field.getSize() && e < numErrors; i++) {
|
||||
if (errorLocator.evaluateAt(i) == 0) {
|
||||
result[e] = field.inverse(i);
|
||||
e++;
|
||||
}
|
||||
}
|
||||
if (e != numErrors) {
|
||||
throw ChecksumException.getChecksumInstance();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int[] findErrorMagnitudes(ModulusPoly errorEvaluator,
|
||||
ModulusPoly errorLocator,
|
||||
int[] errorLocations) {
|
||||
int errorLocatorDegree = errorLocator.getDegree();
|
||||
if (errorLocatorDegree < 1) {
|
||||
return new int[0];
|
||||
}
|
||||
int[] formalDerivativeCoefficients = new int[errorLocatorDegree];
|
||||
for (int i = 1; i <= errorLocatorDegree; i++) {
|
||||
formalDerivativeCoefficients[errorLocatorDegree - i] =
|
||||
field.multiply(i, errorLocator.getCoefficient(i));
|
||||
}
|
||||
ModulusPoly formalDerivative = new ModulusPoly(field, formalDerivativeCoefficients);
|
||||
|
||||
// This is directly applying Forney's Formula
|
||||
int s = errorLocations.length;
|
||||
int[] result = new int[s];
|
||||
for (int i = 0; i < s; i++) {
|
||||
int xiInverse = field.inverse(errorLocations[i]);
|
||||
int numerator = field.subtract(0, errorEvaluator.evaluateAt(xiInverse));
|
||||
int denominator = field.inverse(formalDerivative.evaluateAt(xiInverse));
|
||||
result[i] = field.multiply(numerator, denominator);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 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.pdf417.decoder.ec;
|
||||
|
||||
import com.google.zxing.ChecksumException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class ErrorCorrectionTestCase extends AbstractErrorCorrectionTestCase {
|
||||
|
||||
private static final int[] PDF417_TEST = {
|
||||
48, 901, 56, 141, 627, 856, 330, 69, 244, 900, 852, 169, 843, 895, 852, 895, 913, 154, 845, 778, 387, 89, 869,
|
||||
901, 219, 474, 543, 650, 169, 201, 9, 160, 35, 70, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900,
|
||||
900, 900};
|
||||
private static final int[] PDF417_TEST_WITH_EC = {
|
||||
48, 901, 56, 141, 627, 856, 330, 69, 244, 900, 852, 169, 843, 895, 852, 895, 913, 154, 845, 778, 387, 89, 869,
|
||||
901, 219, 474, 543, 650, 169, 201, 9, 160, 35, 70, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900,
|
||||
900, 900, 769, 843, 591, 910, 605, 206, 706, 917, 371, 469, 79, 718, 47, 777, 249, 262, 193, 620, 597, 477, 450,
|
||||
806, 908, 309, 153, 871, 686, 838, 185, 674, 68, 679, 691, 794, 497, 479, 234, 250, 496, 43, 347, 582, 882, 536,
|
||||
322, 317, 273, 194, 917, 237, 420, 859, 340, 115, 222, 808, 866, 836, 417, 121, 833, 459, 64, 159};
|
||||
private static final int ECC_BYTES = PDF417_TEST_WITH_EC.length - PDF417_TEST.length;
|
||||
private static final int ERROR_LIMIT = ECC_BYTES;
|
||||
private static final int MAX_ERRORS = ERROR_LIMIT / 2;
|
||||
private static final int MAX_ERASURES = ERROR_LIMIT;
|
||||
|
||||
private final ErrorCorrection ec = new ErrorCorrection();
|
||||
|
||||
@Test
|
||||
public void testNoError() throws ChecksumException {
|
||||
int[] received = PDF417_TEST_WITH_EC.clone();
|
||||
// no errors
|
||||
checkDecode(received);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneError() throws ChecksumException {
|
||||
Random random = getRandom();
|
||||
for (int i = 0; i < PDF417_TEST_WITH_EC.length; i++) {
|
||||
int[] received = PDF417_TEST_WITH_EC.clone();
|
||||
received[i] = random.nextInt(256);
|
||||
checkDecode(received);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxErrors() throws ChecksumException {
|
||||
Random random = getRandom();
|
||||
for (int testIterations = 0; testIterations < 100; testIterations++) { // # iterations is kind of arbitrary
|
||||
int[] received = PDF417_TEST_WITH_EC.clone();
|
||||
corrupt(received, MAX_ERRORS, random);
|
||||
checkDecode(received);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTooManyErrors() {
|
||||
int[] received = PDF417_TEST_WITH_EC.clone();
|
||||
Random random = getRandom();
|
||||
corrupt(received, MAX_ERRORS + 1, random);
|
||||
try {
|
||||
checkDecode(received);
|
||||
fail("Should not have decoded");
|
||||
} catch (ChecksumException ce) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDecode(int[] received) throws ChecksumException {
|
||||
checkDecode(received, new int[0]);
|
||||
}
|
||||
|
||||
private void checkDecode(int[] received, int[] erasures) throws ChecksumException {
|
||||
ec.decode(received, ECC_BYTES, erasures);
|
||||
for (int i = 0; i < PDF417_TEST.length; i++) {
|
||||
assertEquals(received[i], PDF417_TEST[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 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.pdf417.decoder.ec;
|
||||
|
||||
import com.google.zxing.pdf417.PDF417Common;
|
||||
|
||||
/**
|
||||
* <p>A field based on powers of a generator integer, modulo some modulus.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see com.google.zxing.common.reedsolomon.GenericGF
|
||||
*/
|
||||
public final class ModulusGF {
|
||||
|
||||
public static final ModulusGF PDF417_GF = new ModulusGF(PDF417Common.NUMBER_OF_CODEWORDS, 3);
|
||||
|
||||
private final int[] expTable;
|
||||
private final int[] logTable;
|
||||
private final ModulusPoly zero;
|
||||
private final ModulusPoly one;
|
||||
private final int modulus;
|
||||
|
||||
private ModulusGF(int modulus, int generator) {
|
||||
this.modulus = modulus;
|
||||
expTable = new int[modulus];
|
||||
logTable = new int[modulus];
|
||||
int x = 1;
|
||||
for (int i = 0; i < modulus; i++) {
|
||||
expTable[i] = x;
|
||||
x = (x * generator) % modulus;
|
||||
}
|
||||
for (int i = 0; i < modulus - 1; i++) {
|
||||
logTable[expTable[i]] = i;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
zero = new ModulusPoly(this, new int[]{0});
|
||||
one = new ModulusPoly(this, new int[]{1});
|
||||
}
|
||||
|
||||
|
||||
ModulusPoly getZero() {
|
||||
return zero;
|
||||
}
|
||||
|
||||
ModulusPoly getOne() {
|
||||
return one;
|
||||
}
|
||||
|
||||
ModulusPoly 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 ModulusPoly(this, coefficients);
|
||||
}
|
||||
|
||||
int add(int a, int b) {
|
||||
return (a + b) % modulus;
|
||||
}
|
||||
|
||||
int subtract(int a, int b) {
|
||||
return (modulus + a - b) % modulus;
|
||||
}
|
||||
|
||||
int exp(int a) {
|
||||
return expTable[a];
|
||||
}
|
||||
|
||||
int log(int a) {
|
||||
if (a == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return logTable[a];
|
||||
}
|
||||
|
||||
int inverse(int a) {
|
||||
if (a == 0) {
|
||||
throw new ArithmeticException();
|
||||
}
|
||||
return expTable[modulus - logTable[a] - 1];
|
||||
}
|
||||
|
||||
int multiply(int a, int b) {
|
||||
if (a == 0 || b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return expTable[(logTable[a] + logTable[b]) % (modulus - 1)];
|
||||
}
|
||||
|
||||
int getSize() {
|
||||
return modulus;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 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.pdf417.decoder.ec;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class ModulusPoly {
|
||||
|
||||
private final ModulusGF field;
|
||||
private final int[] coefficients;
|
||||
|
||||
ModulusPoly(ModulusGF field, int[] coefficients) {
|
||||
if (coefficients.length == 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.field = field;
|
||||
int coefficientsLength = coefficients.length;
|
||||
if (coefficientsLength > 1 && coefficients[0] == 0) {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
int firstNonZero = 1;
|
||||
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
|
||||
firstNonZero++;
|
||||
}
|
||||
if (firstNonZero == coefficientsLength) {
|
||||
this.coefficients = new int[]{0};
|
||||
} else {
|
||||
this.coefficients = new int[coefficientsLength - firstNonZero];
|
||||
System.arraycopy(coefficients,
|
||||
firstNonZero,
|
||||
this.coefficients,
|
||||
0,
|
||||
this.coefficients.length);
|
||||
}
|
||||
} else {
|
||||
this.coefficients = coefficients;
|
||||
}
|
||||
}
|
||||
|
||||
int[] getCoefficients() {
|
||||
return coefficients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return degree of this polynomial
|
||||
*/
|
||||
int getDegree() {
|
||||
return coefficients.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this polynomial is the monomial "0"
|
||||
*/
|
||||
boolean isZero() {
|
||||
return coefficients[0] == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return coefficient of x^degree term in this polynomial
|
||||
*/
|
||||
int getCoefficient(int degree) {
|
||||
return coefficients[coefficients.length - 1 - degree];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return evaluation of this polynomial at a given point
|
||||
*/
|
||||
int evaluateAt(int a) {
|
||||
if (a == 0) {
|
||||
// Just return the x^0 coefficient
|
||||
return getCoefficient(0);
|
||||
}
|
||||
if (a == 1) {
|
||||
// Just the sum of the coefficients
|
||||
int result = 0;
|
||||
for (int coefficient : coefficients) {
|
||||
result = field.add(result, coefficient);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
int result = coefficients[0];
|
||||
int size = coefficients.length;
|
||||
for (int i = 1; i < size; i++) {
|
||||
result = field.add(field.multiply(a, result), coefficients[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ModulusPoly add(ModulusPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("ModulusPolys do not have same ModulusGF field");
|
||||
}
|
||||
if (isZero()) {
|
||||
return other;
|
||||
}
|
||||
if (other.isZero()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
int[] smallerCoefficients = this.coefficients;
|
||||
int[] largerCoefficients = other.coefficients;
|
||||
if (smallerCoefficients.length > largerCoefficients.length) {
|
||||
int[] temp = smallerCoefficients;
|
||||
smallerCoefficients = largerCoefficients;
|
||||
largerCoefficients = temp;
|
||||
}
|
||||
int[] sumDiff = new int[largerCoefficients.length];
|
||||
int lengthDiff = largerCoefficients.length - smallerCoefficients.length;
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
|
||||
|
||||
for (int i = lengthDiff; i < largerCoefficients.length; i++) {
|
||||
sumDiff[i] = field.add(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
||||
}
|
||||
|
||||
return new ModulusPoly(field, sumDiff);
|
||||
}
|
||||
|
||||
ModulusPoly subtract(ModulusPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("ModulusPolys do not have same ModulusGF field");
|
||||
}
|
||||
if (other.isZero()) {
|
||||
return this;
|
||||
}
|
||||
return add(other.negative());
|
||||
}
|
||||
|
||||
ModulusPoly multiply(ModulusPoly other) {
|
||||
if (!field.equals(other.field)) {
|
||||
throw new IllegalArgumentException("ModulusPolys do not have same ModulusGF field");
|
||||
}
|
||||
if (isZero() || other.isZero()) {
|
||||
return field.getZero();
|
||||
}
|
||||
int[] aCoefficients = this.coefficients;
|
||||
int aLength = aCoefficients.length;
|
||||
int[] bCoefficients = other.coefficients;
|
||||
int bLength = bCoefficients.length;
|
||||
int[] product = new int[aLength + bLength - 1];
|
||||
for (int i = 0; i < aLength; i++) {
|
||||
int aCoeff = aCoefficients[i];
|
||||
for (int j = 0; j < bLength; j++) {
|
||||
product[i + j] = field.add(product[i + j], field.multiply(aCoeff, bCoefficients[j]));
|
||||
}
|
||||
}
|
||||
return new ModulusPoly(field, product);
|
||||
}
|
||||
|
||||
ModulusPoly negative() {
|
||||
int size = coefficients.length;
|
||||
int[] negativeCoefficients = new int[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
negativeCoefficients[i] = field.subtract(0, coefficients[i]);
|
||||
}
|
||||
return new ModulusPoly(field, negativeCoefficients);
|
||||
}
|
||||
|
||||
ModulusPoly multiply(int scalar) {
|
||||
if (scalar == 0) {
|
||||
return field.getZero();
|
||||
}
|
||||
if (scalar == 1) {
|
||||
return this;
|
||||
}
|
||||
int size = coefficients.length;
|
||||
int[] product = new int[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
product[i] = field.multiply(coefficients[i], scalar);
|
||||
}
|
||||
return new ModulusPoly(field, product);
|
||||
}
|
||||
|
||||
ModulusPoly multiplyByMonomial(int degree, int coefficient) {
|
||||
if (degree < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (coefficient == 0) {
|
||||
return field.getZero();
|
||||
}
|
||||
int size = coefficients.length;
|
||||
int[] product = new int[size + degree];
|
||||
for (int i = 0; i < size; i++) {
|
||||
product[i] = field.multiply(coefficients[i], coefficient);
|
||||
}
|
||||
return new ModulusPoly(field, product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(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) {
|
||||
result.append(coefficient);
|
||||
}
|
||||
if (degree != 0) {
|
||||
if (degree == 1) {
|
||||
result.append('x');
|
||||
} else {
|
||||
result.append("x^");
|
||||
result.append(degree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
56
src/pdf417/decoder/ec/abstract_error_correction_test_case.rs
Normal file
56
src/pdf417/decoder/ec/abstract_error_correction_test_case.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use crate::common::reedsolomon::ReedSolomonTestCase;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
|
||||
pub fn corrupt(received: &mut [u32], howMany: u32, random: &mut rand::rngs::ThreadRng) {
|
||||
let mut pass: Vec<i32> = received.iter().map(|x| *x as i32).collect();
|
||||
ReedSolomonTestCase::corrupt(&mut pass, howMany as i32, random, 929);
|
||||
for i in 0..received.len() {
|
||||
received[i] = pass[i] as u32;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn erase(received: &mut [u32], howMany: u32, random: &mut rand::rngs::ThreadRng) -> Vec<u32> {
|
||||
let mut erased = vec![false; received.len()]; //BitSet::new(received.len());
|
||||
let mut erasures = vec![0_u32; howMany as usize];
|
||||
let mut erasureOffset = 0;
|
||||
let mut j = 0;
|
||||
while j < howMany {
|
||||
// for (int j = 0; j < howMany; j++) {
|
||||
let location = random.gen_range(0..received.len()); //random.nextInt(received.len());
|
||||
if *erased.get(location).unwrap() {
|
||||
j -= 1;
|
||||
} else {
|
||||
erased[location] = true;
|
||||
received[location] = 0;
|
||||
erasures[erasureOffset] = location as u32;
|
||||
erasureOffset += 1;
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
erasures
|
||||
}
|
||||
|
||||
pub fn getRandom() -> rand::rngs::ThreadRng {
|
||||
rand::thread_rng()
|
||||
}
|
||||
235
src/pdf417/decoder/ec/error_correction.rs
Normal file
235
src/pdf417/decoder/ec/error_correction.rs
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{
|
||||
pdf417::{decoder::ec::ModulusGF, pdf_417_common::NUMBER_OF_CODEWORDS},
|
||||
Exceptions,
|
||||
};
|
||||
|
||||
use super::ModulusPoly;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
// static ref PDF417_GF : Rc<&ModulusGF> = Rc::new(&ModulusGF::new(NUMBER_OF_CODEWORDS, 3));
|
||||
static ref fld_interior : ModulusGF = ModulusGF::new(NUMBER_OF_CODEWORDS, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>PDF417 error correction implementation.</p>
|
||||
*
|
||||
* <p>This <a href="http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction#Example">example</a>
|
||||
* is quite useful in understanding the algorithm.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see com.google.zxing.common.reedsolomon.ReedSolomonDecoder
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param received received codewords
|
||||
* @param numECCodewords number of those codewords used for EC
|
||||
* @param erasures location of erasures
|
||||
* @return number of errors
|
||||
* @throws ChecksumException if errors cannot be corrected, maybe because of too many errors
|
||||
*/
|
||||
pub fn decode<'a>(
|
||||
received: &mut [u32],
|
||||
numECCodewords: u32,
|
||||
erasures: &mut [u32],
|
||||
) -> Result<usize, Exceptions> {
|
||||
let field: Rc<&'static ModulusGF> = Rc::new(&fld_interior);
|
||||
let poly = ModulusPoly::new(field.clone(), received.to_vec())?;
|
||||
let mut S = vec![0u32; numECCodewords as usize];
|
||||
let mut error = false;
|
||||
for i in (1..=numECCodewords).rev() {
|
||||
// for (int i = numECCodewords; i > 0; i--) {
|
||||
let eval = poly.evaluateAt(field.exp(i));
|
||||
S[(numECCodewords - i) as usize] = eval;
|
||||
if eval != 0 {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !error {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let mut knownErrors: Rc<ModulusPoly> = ModulusPoly::getOne(field.clone());
|
||||
let mut b;
|
||||
let mut term;
|
||||
let mut kE: Rc<ModulusPoly>;
|
||||
if !erasures.is_empty() {
|
||||
for erasure in erasures {
|
||||
// for (int erasure : erasures) {
|
||||
b = field.exp(received.len() as u32 - 1 - *erasure);
|
||||
// Add (1 - bx) term:
|
||||
term = ModulusPoly::new(field.clone(), vec![field.subtract(0, b), 1])?;
|
||||
kE = knownErrors.clone();
|
||||
knownErrors = kE.multiply(Rc::new(term))?;
|
||||
}
|
||||
}
|
||||
|
||||
let syndrome = Rc::new(ModulusPoly::new(field.clone(), S)?);
|
||||
//syndrome = syndrome.multiply(knownErrors);
|
||||
|
||||
let sigmaOmega = runEuclideanAlgorithm(
|
||||
ModulusPoly::buildMonomial(field.clone(), numECCodewords as usize, 1),
|
||||
syndrome,
|
||||
numECCodewords,
|
||||
field.clone(),
|
||||
)?;
|
||||
let sigma = sigmaOmega[0].clone();
|
||||
let omega = sigmaOmega[1].clone();
|
||||
|
||||
//sigma = sigma.multiply(knownErrors);
|
||||
|
||||
let mut errorLocations = findErrorLocations(sigma.clone(), field.clone())?;
|
||||
let errorMagnitudes = findErrorMagnitudes(omega, sigma, &mut errorLocations, field.clone());
|
||||
|
||||
for i in 0..errorLocations.len() {
|
||||
// for (int i = 0; i < errorLocations.length; i++) {
|
||||
let position = received.len() as isize - 1 - field.log(errorLocations[i])? as isize;
|
||||
if position < 0 {
|
||||
return Err(Exceptions::ChecksumException(format!("{}", file!())));
|
||||
}
|
||||
received[position as usize] =
|
||||
field.subtract(received[position as usize], errorMagnitudes[i]);
|
||||
}
|
||||
|
||||
Ok(errorLocations.len())
|
||||
}
|
||||
|
||||
fn runEuclideanAlgorithm(
|
||||
a: Rc<ModulusPoly>,
|
||||
b: Rc<ModulusPoly>,
|
||||
R: u32,
|
||||
field: Rc<&'static ModulusGF>,
|
||||
) -> Result<[Rc<ModulusPoly>; 2], Exceptions> {
|
||||
// Assume a's degree is >= b's
|
||||
let mut a = a;
|
||||
let mut b = b;
|
||||
if a.getDegree() < b.getDegree() {
|
||||
let temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
let mut rLast = a;
|
||||
let mut r = b;
|
||||
let mut tLast = ModulusPoly::getZero(field.clone());
|
||||
let mut t = ModulusPoly::getOne(field.clone());
|
||||
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while r.getDegree() >= R / 2 {
|
||||
let rLastLast = rLast.clone();
|
||||
let tLastLast = tLast.clone();
|
||||
rLast = r;
|
||||
tLast = t;
|
||||
|
||||
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||
if rLast.isZero() {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
return Err(Exceptions::ChecksumException(format!("{}", file!())));
|
||||
}
|
||||
r = rLastLast;
|
||||
let mut q = ModulusPoly::getZero(field.clone()); //field.getZero();
|
||||
let denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree() as usize);
|
||||
let dltInverse = field.inverse(denominatorLeadingTerm)?;
|
||||
while r.getDegree() >= rLast.getDegree() && !r.isZero() {
|
||||
let degreeDiff = r.getDegree() - rLast.getDegree();
|
||||
let scale = field.multiply(r.getCoefficient(r.getDegree() as usize), dltInverse);
|
||||
q = q.add(ModulusPoly::buildMonomial(
|
||||
field.clone(),
|
||||
degreeDiff as usize,
|
||||
scale,
|
||||
))?;
|
||||
r = r.subtract(rLast.multiplyByMonomial(degreeDiff as usize, scale))?;
|
||||
}
|
||||
|
||||
t = q.multiply(tLast.clone())?.subtract(tLastLast)?.negative();
|
||||
}
|
||||
|
||||
let sigmaTildeAtZero = t.getCoefficient(0);
|
||||
if sigmaTildeAtZero == 0 {
|
||||
return Err(Exceptions::ChecksumException(format!("{}", file!())));
|
||||
}
|
||||
|
||||
let inverse = field.inverse(sigmaTildeAtZero)?;
|
||||
let sigma = t.multiplyByScaler(inverse);
|
||||
let omega = r.multiplyByScaler(inverse);
|
||||
|
||||
Ok([sigma, omega])
|
||||
}
|
||||
|
||||
fn findErrorLocations(
|
||||
errorLocator: Rc<ModulusPoly>,
|
||||
field: Rc<&ModulusGF>,
|
||||
) -> Result<Vec<u32>, Exceptions> {
|
||||
// This is a direct application of Chien's search
|
||||
let numErrors = errorLocator.getDegree();
|
||||
let mut result = vec![0u32; numErrors as usize];
|
||||
let mut e = 0;
|
||||
let mut i = 1;
|
||||
while i < field.getSize() && e < numErrors {
|
||||
// for (int i = 1; i < PDF417_GF.getSize() && e < numErrors; i++) {
|
||||
if errorLocator.evaluateAt(i) == 0 {
|
||||
result[e as usize] = field.inverse(i)?;
|
||||
e += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if e != numErrors {
|
||||
return Err(Exceptions::ChecksumException(format!("{}", file!())));
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn findErrorMagnitudes(
|
||||
errorEvaluator: Rc<ModulusPoly>,
|
||||
errorLocator: Rc<ModulusPoly>,
|
||||
errorLocations: &mut [u32],
|
||||
field: Rc<&'static ModulusGF>,
|
||||
) -> Vec<u32> {
|
||||
let errorLocatorDegree = errorLocator.getDegree();
|
||||
if errorLocatorDegree < 1 {
|
||||
return vec![0; 0];
|
||||
}
|
||||
let mut formalDerivativeCoefficients = vec![0u32; errorLocatorDegree as usize];
|
||||
for i in 1..=errorLocatorDegree {
|
||||
// for (int i = 1; i <= errorLocatorDegree; i++) {
|
||||
formalDerivativeCoefficients[errorLocatorDegree as usize - i as usize] =
|
||||
field.multiply(i, errorLocator.getCoefficient(i as usize));
|
||||
}
|
||||
let formalDerivative = ModulusPoly::new(field.clone(), formalDerivativeCoefficients)
|
||||
.expect("should generate good poly");
|
||||
|
||||
// This is directly applying Forney's Formula
|
||||
let s = errorLocations.len();
|
||||
let mut result = vec![0u32; s];
|
||||
for i in 0..s {
|
||||
// for (int i = 0; i < s; i++) {
|
||||
let xiInverse = field.inverse(errorLocations[i]).expect("must invert");
|
||||
let numerator = field.subtract(0, errorEvaluator.evaluateAt(xiInverse));
|
||||
let denominator = field
|
||||
.inverse(formalDerivative.evaluateAt(xiInverse))
|
||||
.expect("must invert");
|
||||
result[i] = field.multiply(numerator, denominator);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
115
src/pdf417/decoder/ec/error_correction_test_case.rs
Normal file
115
src/pdf417/decoder/ec/error_correction_test_case.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use crate::{datamatrix::encoder::error_correction, Exceptions};
|
||||
|
||||
use super::{
|
||||
abstract_error_correction_test_case::{corrupt, getRandom},
|
||||
error_correction::decode,
|
||||
};
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
|
||||
const PDF417_TEST: [u32; 48] = [
|
||||
48, 901, 56, 141, 627, 856, 330, 69, 244, 900, 852, 169, 843, 895, 852, 895, 913, 154, 845,
|
||||
778, 387, 89, 869, 901, 219, 474, 543, 650, 169, 201, 9, 160, 35, 70, 900, 900, 900, 900, 900,
|
||||
900, 900, 900, 900, 900, 900, 900, 900, 900,
|
||||
];
|
||||
const PDF417_TEST_WITH_EC: [u32; 112] = [
|
||||
48, 901, 56, 141, 627, 856, 330, 69, 244, 900, 852, 169, 843, 895, 852, 895, 913, 154, 845,
|
||||
778, 387, 89, 869, 901, 219, 474, 543, 650, 169, 201, 9, 160, 35, 70, 900, 900, 900, 900, 900,
|
||||
900, 900, 900, 900, 900, 900, 900, 900, 900, 769, 843, 591, 910, 605, 206, 706, 917, 371, 469,
|
||||
79, 718, 47, 777, 249, 262, 193, 620, 597, 477, 450, 806, 908, 309, 153, 871, 686, 838, 185,
|
||||
674, 68, 679, 691, 794, 497, 479, 234, 250, 496, 43, 347, 582, 882, 536, 322, 317, 273, 194,
|
||||
917, 237, 420, 859, 340, 115, 222, 808, 866, 836, 417, 121, 833, 459, 64, 159,
|
||||
];
|
||||
const ECC_BYTES: usize = PDF417_TEST_WITH_EC.len() - PDF417_TEST.len();
|
||||
const ERROR_LIMIT: usize = ECC_BYTES;
|
||||
const MAX_ERRORS: usize = ERROR_LIMIT / 2;
|
||||
const _MAX_ERASURES: usize = ERROR_LIMIT;
|
||||
|
||||
// private final ErrorCorrection ec = new ErrorCorrection();
|
||||
|
||||
#[test]
|
||||
fn testNoError() {
|
||||
let mut received = PDF417_TEST_WITH_EC.clone();
|
||||
// no errors
|
||||
checkDecode(&mut received).expect("ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn testExplicitError() {
|
||||
let mut random = getRandom();
|
||||
for i in 0..PDF417_TEST_WITH_EC.len() {
|
||||
// for (int i = 0; i < PDF417_TEST_WITH_EC.length; i++) {
|
||||
let mut received = PDF417_TEST_WITH_EC.clone();
|
||||
received[i] = 610; //random.gen_range(0..256);// random.nextInt(256);
|
||||
checkDecode(&mut received).expect("ok");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn testOneError() {
|
||||
let mut random = getRandom();
|
||||
for i in 0..PDF417_TEST_WITH_EC.len() {
|
||||
// for (int i = 0; i < PDF417_TEST_WITH_EC.length; i++) {
|
||||
let mut received = PDF417_TEST_WITH_EC.clone();
|
||||
received[i] = random.gen_range(0..256); //random.gen_range(0..256);// random.nextInt(256);
|
||||
checkDecode(&mut received).expect("ok");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn testMaxErrors() {
|
||||
let mut random = getRandom();
|
||||
for _testIterations in 0..100 {
|
||||
// for (int testIterations = 0; testIterations < 100; testIterations++) { // # iterations is kind of arbitrary
|
||||
let mut received = PDF417_TEST_WITH_EC.clone();
|
||||
corrupt(&mut received, MAX_ERRORS as u32, &mut random);
|
||||
checkDecode(&mut received).expect("ok");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn testTooManyErrors() {
|
||||
let mut received = PDF417_TEST_WITH_EC.clone();
|
||||
let mut random = getRandom();
|
||||
corrupt(&mut received, MAX_ERRORS as u32 + 1, &mut random);
|
||||
// try {
|
||||
assert!(checkDecode(&mut received).is_err());
|
||||
// fail("Should not have decoded");
|
||||
// } catch (ChecksumException ce) {
|
||||
// // good
|
||||
// }
|
||||
}
|
||||
|
||||
fn checkDecode(received: &mut [u32]) -> Result<(), Exceptions> {
|
||||
checkDecodeErasures(received, &mut [0_u32; 0])
|
||||
}
|
||||
|
||||
fn checkDecodeErasures(received: &mut [u32], erasures: &mut [u32]) -> Result<(), Exceptions> {
|
||||
decode(received, ECC_BYTES as u32, erasures)?;
|
||||
// ec.decode(received, ECC_BYTES, erasures);
|
||||
for i in 0..PDF417_TEST.len() {
|
||||
// for (int i = 0; i < PDF417_TEST.length; i++) {
|
||||
assert_eq!(received[i], PDF417_TEST[i]);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1 +1,12 @@
|
||||
mod modulus_gf;
|
||||
pub use modulus_gf::*;
|
||||
|
||||
mod modulus_poly;
|
||||
pub use modulus_poly::*;
|
||||
|
||||
pub mod error_correction;
|
||||
|
||||
#[cfg(test)]
|
||||
mod abstract_error_correction_test_case;
|
||||
#[cfg(test)]
|
||||
mod error_correction_test_case;
|
||||
|
||||
117
src/pdf417/decoder/ec/modulus_gf.rs
Normal file
117
src/pdf417/decoder/ec/modulus_gf.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
//public static final ModulusGF PDF417_GF = new ModulusGF(PDF417Common.NUMBER_OF_CODEWORDS, 3);
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::Exceptions;
|
||||
|
||||
use super::ModulusPoly;
|
||||
|
||||
/**
|
||||
* <p>A field based on powers of a generator integer, modulo some modulus.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see com.google.zxing.common.reedsolomon.GenericGF
|
||||
*/
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModulusGF {
|
||||
expTable: Vec<u32>,
|
||||
logTable: Vec<u32>,
|
||||
// zero: Option<Rc<ModulusPoly<'a>>>,
|
||||
// one: Option<Rc<ModulusPoly<'a>>>,
|
||||
modulus: u32,
|
||||
generator: u32,
|
||||
}
|
||||
impl ModulusGF {
|
||||
pub fn new(modulus: u32, generator: u32) -> Self {
|
||||
let mut expTable = vec![0u32; modulus as usize]; //new int[modulus];
|
||||
let mut logTable = vec![0u32; modulus as usize]; //new int[modulus];
|
||||
let mut x = 1;
|
||||
for i in 0..modulus as usize {
|
||||
// for (int i = 0; i < modulus; i++) {
|
||||
expTable[i] = x;
|
||||
x = (x * generator) % modulus;
|
||||
}
|
||||
for i in 0..modulus as usize - 1 {
|
||||
// for (int i = 0; i < modulus - 1; i++) {
|
||||
logTable[expTable[i] as usize] = i as u32;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
|
||||
let potential_self = Self {
|
||||
expTable,
|
||||
logTable,
|
||||
// zero: None,
|
||||
// one: None,
|
||||
modulus,
|
||||
generator,
|
||||
};
|
||||
// zero = new ModulusPoly(this, new int[]{0});
|
||||
// one = new ModulusPoly(this, new int[]{1});
|
||||
|
||||
potential_self
|
||||
}
|
||||
|
||||
pub fn add(&self, a: u32, b: u32) -> u32 {
|
||||
(a + b) % self.modulus
|
||||
}
|
||||
|
||||
pub fn subtract(&self, a: u32, b: u32) -> u32 {
|
||||
(self.modulus + a - b) % self.modulus
|
||||
}
|
||||
|
||||
pub fn exp(&self, a: u32) -> u32 {
|
||||
self.expTable[a as usize]
|
||||
}
|
||||
|
||||
pub fn log(&self, a: u32) -> Result<u32, Exceptions> {
|
||||
if a == 0 {
|
||||
Err(Exceptions::ArithmeticException("".to_owned()))
|
||||
} else {
|
||||
Ok(self.logTable[a as usize])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inverse(&self, a: u32) -> Result<u32, Exceptions> {
|
||||
if a == 0 {
|
||||
Err(Exceptions::ArithmeticException("".to_owned()))
|
||||
} else {
|
||||
Ok(self.expTable[self.modulus as usize - self.logTable[a as usize] as usize - 1])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn multiply(&self, a: u32, b: u32) -> u32 {
|
||||
if a == 0 || b == 0 {
|
||||
0
|
||||
} else {
|
||||
self.expTable[(self.logTable[a as usize] + self.logTable[b as usize]) as usize
|
||||
% (self.modulus - 1) as usize]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getSize(&self) -> u32 {
|
||||
self.modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ModulusGF {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.modulus == other.modulus && self.generator == other.generator
|
||||
}
|
||||
}
|
||||
impl Eq for ModulusGF {}
|
||||
321
src/pdf417/decoder/ec/modulus_poly.rs
Normal file
321
src/pdf417/decoder/ec/modulus_poly.rs
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::Exceptions;
|
||||
|
||||
use super::ModulusGF;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ModulusPoly {
|
||||
field: Rc<&'static ModulusGF>,
|
||||
coefficients: Vec<u32>,
|
||||
// zero: Option<Rc<ModulusPoly>>,
|
||||
// one: Option<Rc<ModulusPoly>>,
|
||||
}
|
||||
impl ModulusPoly {
|
||||
pub fn new(
|
||||
field: Rc<&'static ModulusGF>,
|
||||
coefficients: Vec<u32>,
|
||||
) -> Result<ModulusPoly, Exceptions> {
|
||||
if coefficients.is_empty() {
|
||||
return Err(Exceptions::IllegalArgumentException("".to_owned()));
|
||||
}
|
||||
let orig_coefs = coefficients.clone();
|
||||
let mut coefficients = coefficients;
|
||||
let coefficientsLength = coefficients.len();
|
||||
if coefficientsLength > 1 && coefficients[0] == 0 {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
let mut firstNonZero = 1;
|
||||
while firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0 {
|
||||
firstNonZero += 1;
|
||||
}
|
||||
if firstNonZero == coefficientsLength {
|
||||
coefficients = vec![0];
|
||||
} else {
|
||||
coefficients = vec![0u32; coefficientsLength - firstNonZero];
|
||||
coefficients[..].copy_from_slice(&&orig_coefs[firstNonZero..]);
|
||||
// System.arraycopy(coefficients,
|
||||
// firstNonZero,
|
||||
// this.coefficients,
|
||||
// 0,
|
||||
// this.coefficients.length);
|
||||
}
|
||||
} else {
|
||||
coefficients = coefficients;
|
||||
}
|
||||
|
||||
Ok(ModulusPoly {
|
||||
field: field.clone(),
|
||||
coefficients: coefficients,
|
||||
// zero: Some(Self::getZero(field.clone())),
|
||||
// one: Some(Self::getOne(field.clone())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn getCoefficients(&self) -> &[u32] {
|
||||
&self.coefficients
|
||||
}
|
||||
|
||||
/**
|
||||
* @return degree of this polynomial
|
||||
*/
|
||||
pub fn getDegree(&self) -> u32 {
|
||||
self.coefficients.len() as u32 - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this polynomial is the monomial "0"
|
||||
*/
|
||||
pub fn isZero(&self) -> bool {
|
||||
self.coefficients[0] == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @return coefficient of x^degree term in this polynomial
|
||||
*/
|
||||
pub fn getCoefficient(&self, degree: usize) -> u32 {
|
||||
self.coefficients[self.coefficients.len() - 1 - degree]
|
||||
}
|
||||
|
||||
/**
|
||||
* @return evaluation of this polynomial at a given point
|
||||
*/
|
||||
pub fn evaluateAt(&self, a: u32) -> u32 {
|
||||
if a == 0 {
|
||||
// Just return the x^0 coefficient
|
||||
return self.getCoefficient(0);
|
||||
}
|
||||
if a == 1 {
|
||||
// Just the sum of the coefficients
|
||||
let mut result = 0;
|
||||
for coefficient in self.coefficients.iter() {
|
||||
// for (int coefficient : coefficients) {
|
||||
result = self.field.add(result, *coefficient);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let mut result = self.coefficients[0];
|
||||
let size = self.coefficients.len();
|
||||
for i in 1..size {
|
||||
// for (int i = 1; i < size; i++) {
|
||||
result = self
|
||||
.field
|
||||
.add(self.field.multiply(a, result), self.coefficients[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn add(&self, other: Rc<ModulusPoly>) -> Result<Rc<ModulusPoly>, Exceptions> {
|
||||
if self.field != other.field {
|
||||
return Err(Exceptions::IllegalArgumentException(
|
||||
"ModulusPolys do not have same ModulusGF field".to_owned(),
|
||||
));
|
||||
}
|
||||
if self.isZero() {
|
||||
return Ok(other);
|
||||
}
|
||||
if other.isZero() {
|
||||
return Ok(Rc::new(self.clone()));
|
||||
}
|
||||
|
||||
let mut smallerCoefficients = &self.coefficients;
|
||||
let mut largerCoefficients = &other.coefficients;
|
||||
if smallerCoefficients.len() > largerCoefficients.len() {
|
||||
let temp = smallerCoefficients;
|
||||
smallerCoefficients = largerCoefficients;
|
||||
largerCoefficients = temp;
|
||||
}
|
||||
let mut sumDiff = vec![0032; largerCoefficients.len()];
|
||||
let lengthDiff = largerCoefficients.len() - smallerCoefficients.len();
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
sumDiff[..lengthDiff].copy_from_slice(&largerCoefficients[..lengthDiff]);
|
||||
// System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
|
||||
|
||||
for i in lengthDiff..largerCoefficients.len() {
|
||||
// for (int i = lengthDiff; i < largerCoefficients.length; i++) {
|
||||
sumDiff[i] = self
|
||||
.field
|
||||
.add(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
|
||||
}
|
||||
|
||||
Ok(Rc::new(
|
||||
ModulusPoly::new(self.field.clone(), sumDiff)
|
||||
.expect("should always generate with known goods"),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn subtract(&self, other: Rc<ModulusPoly>) -> Result<Rc<ModulusPoly>, Exceptions> {
|
||||
if self.field != other.field {
|
||||
return Err(Exceptions::IllegalArgumentException(
|
||||
"ModulusPolys do not have same ModulusGF field".to_owned(),
|
||||
));
|
||||
}
|
||||
if other.isZero() {
|
||||
return Ok(Rc::new(self.clone()));
|
||||
};
|
||||
self.add(other.negative().clone())
|
||||
}
|
||||
|
||||
pub fn multiply(&self, other: Rc<ModulusPoly>) -> Result<Rc<ModulusPoly>, Exceptions> {
|
||||
if !(self.field == other.field) {
|
||||
return Err(Exceptions::IllegalArgumentException(
|
||||
"ModulusPolys do not have same ModulusGF field".to_owned(),
|
||||
));
|
||||
}
|
||||
if self.isZero() || other.isZero() {
|
||||
return Ok(Self::getZero(self.field.clone()).clone());
|
||||
}
|
||||
let aCoefficients = &self.coefficients;
|
||||
let aLength = aCoefficients.len();
|
||||
let bCoefficients = &other.coefficients;
|
||||
let bLength = bCoefficients.len();
|
||||
let mut product = vec![0u32; aLength + bLength - 1];
|
||||
for i in 0..aLength {
|
||||
// for (int i = 0; i < aLength; i++) {
|
||||
let aCoeff = aCoefficients[i];
|
||||
for j in 0..bLength {
|
||||
// for (int j = 0; j < bLength; j++) {
|
||||
product[i + j] = self.field.add(
|
||||
product[i + j],
|
||||
self.field.multiply(aCoeff, bCoefficients[j]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Rc::new(
|
||||
ModulusPoly::new(self.field.clone(), product)
|
||||
.expect("should always generate with known goods"),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn negative(&self) -> Rc<ModulusPoly> {
|
||||
let size = self.coefficients.len();
|
||||
let mut negativeCoefficients = vec![0u32; size];
|
||||
for i in 0..size {
|
||||
// for (int i = 0; i < size; i++) {
|
||||
negativeCoefficients[i] = self.field.subtract(0, self.coefficients[i]);
|
||||
}
|
||||
Rc::new(
|
||||
ModulusPoly::new(self.field.clone(), negativeCoefficients)
|
||||
.expect("should always generate with known goods"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn multiplyByScaler(&self, scalar: u32) -> Rc<ModulusPoly> {
|
||||
if scalar == 0 {
|
||||
return Self::getZero(self.field.clone()).clone();
|
||||
}
|
||||
if scalar == 1 {
|
||||
return Rc::new(self.clone());
|
||||
}
|
||||
let size = self.coefficients.len();
|
||||
let mut product = vec![0u32; size];
|
||||
for i in 0..size {
|
||||
// for (int i = 0; i < size; i++) {
|
||||
product[i] = self.field.multiply(self.coefficients[i], scalar);
|
||||
}
|
||||
|
||||
Rc::new(
|
||||
ModulusPoly::new(self.field.clone(), product)
|
||||
.expect("should always generate with known goods"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn multiplyByMonomial(&self, degree: usize, coefficient: u32) -> Rc<ModulusPoly> {
|
||||
if coefficient == 0 {
|
||||
return Self::getZero(self.field.clone()).clone();
|
||||
}
|
||||
let size = self.coefficients.len();
|
||||
let mut product = vec![0u32; size + degree];
|
||||
for i in 0..size {
|
||||
// for (int i = 0; i < size; i++) {
|
||||
product[i] = self.field.multiply(self.coefficients[i], coefficient);
|
||||
}
|
||||
|
||||
Rc::new(
|
||||
ModulusPoly::new(self.field.clone(), product)
|
||||
.expect("should always generate with known goods"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn getZero(field: Rc<&'static ModulusGF>) -> Rc<ModulusPoly> {
|
||||
Rc::new(
|
||||
ModulusPoly::new(field.clone(), vec![0])
|
||||
.expect("should always generate with known goods"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn getOne(field: Rc<&'static ModulusGF>) -> Rc<ModulusPoly> {
|
||||
Rc::new(
|
||||
ModulusPoly::new(field.clone(), vec![1])
|
||||
.expect("should always generate with known goods"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn buildMonomial(
|
||||
field: Rc<&'static ModulusGF>,
|
||||
degree: usize,
|
||||
coefficient: u32,
|
||||
) -> Rc<ModulusPoly> {
|
||||
// if degree < 0 {
|
||||
// throw new IllegalArgumentException();
|
||||
// }
|
||||
if coefficient == 0 {
|
||||
return Self::getZero(field);
|
||||
}
|
||||
let mut coefficients = vec![0_u32; degree + 1];
|
||||
coefficients[0] = coefficient;
|
||||
Rc::new(
|
||||
ModulusPoly::new(field.clone(), coefficients)
|
||||
.expect("should always generate with known goods"),
|
||||
)
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public String toString() {
|
||||
// StringBuilder result = new StringBuilder(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) {
|
||||
// result.append(coefficient);
|
||||
// }
|
||||
// if (degree != 0) {
|
||||
// if (degree == 1) {
|
||||
// result.append('x');
|
||||
// } else {
|
||||
// result.append("x^");
|
||||
// result.append(degree);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return result.toString();
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user