mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-27 21:02:35 +00:00
pdf417 ec + test
This commit is contained in:
@@ -340,7 +340,12 @@ fn test_aztec() {
|
|||||||
test_encode_decode_random(azd12, 3072, 1023);
|
test_encode_decode_random(azd12, 3072, 1023);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corrupt(received: &mut Vec<i32>, howMany: i32, random: &mut rand::rngs::ThreadRng, max: i32) {
|
pub(crate) fn corrupt(
|
||||||
|
received: &mut Vec<i32>,
|
||||||
|
howMany: i32,
|
||||||
|
random: &mut rand::rngs::ThreadRng,
|
||||||
|
max: i32,
|
||||||
|
) {
|
||||||
let mut corrupted = vec![false; received.len()];
|
let mut corrupted = vec![false; received.len()];
|
||||||
//BitSet corrupted = new BitSet(received.length);
|
//BitSet corrupted = new BitSet(received.length);
|
||||||
let mut j = 0isize;
|
let mut j = 0isize;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod GenericGFPolyTestCase;
|
mod GenericGFPolyTestCase;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod ReedSolomonTestCase;
|
pub(crate) mod ReedSolomonTestCase;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2007 ZXing authors
|
* Copyright 2007 ZXing authors
|
||||||
@@ -26,12 +26,13 @@ pub type GenericGFRef = &'static GenericGF;
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref AZTEC_DATA_12: GenericGF = GenericGF::new(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
static ref AZTEC_DATA_12: GenericGF = GenericGF::new(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
||||||
static ref AZTEC_DATA_10: GenericGF = GenericGF::new(0x409, 1024, 1); // x^10 + x^3 + 1
|
static ref AZTEC_DATA_10: GenericGF = GenericGF::new(0x409, 1024, 1); // x^10 + x^3 + 1
|
||||||
static ref AZTEC_DATA_6: GenericGF = GenericGF::new(0x43, 64, 1); // x^6 + x + 1
|
static ref AZTEC_DATA_6: GenericGF = GenericGF::new(0x43, 64, 1); // x^6 + x + 1
|
||||||
static ref AZTEC_PARAM: GenericGF = GenericGF::new(0x13, 16, 1); // x^4 + x + 1
|
static ref AZTEC_PARAM: GenericGF = GenericGF::new(0x13, 16, 1); // x^4 + x + 1
|
||||||
static ref QR_CODE_FIELD_256: GenericGF = GenericGF::new(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
|
static ref QR_CODE_FIELD_256: GenericGF = GenericGF::new(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
|
||||||
static ref DATA_MATRIX_FIELD_256: GenericGF = GenericGF::new(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
|
static ref DATA_MATRIX_FIELD_256: GenericGF = GenericGF::new(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
|
||||||
|
// static ref PDF_417_FIELD: GenericGF = GenericGF::new(3,crate::pdf417::pdf_417_common::NUMBER_OF_CODEWORDS as usize,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub const AZTEC_DATA_12: GenericGF = GenericGF::new(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
// pub const AZTEC_DATA_12: GenericGF = GenericGF::new(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
||||||
@@ -52,6 +53,7 @@ pub enum PredefinedGenericGF {
|
|||||||
DataMatrixField256,
|
DataMatrixField256,
|
||||||
AztecData8,
|
AztecData8,
|
||||||
MaxicodeField64,
|
MaxicodeField64,
|
||||||
|
// PDF417,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replacement for old const options, has the downside of generating new versions whenever one is requested.
|
/// Replacement for old const options, has the downside of generating new versions whenever one is requested.
|
||||||
@@ -65,6 +67,7 @@ pub fn get_predefined_genericgf(request: PredefinedGenericGF) -> GenericGFRef {
|
|||||||
PredefinedGenericGF::DataMatrixField256 | PredefinedGenericGF::AztecData8 => {
|
PredefinedGenericGF::DataMatrixField256 | PredefinedGenericGF::AztecData8 => {
|
||||||
&DATA_MATRIX_FIELD_256
|
&DATA_MATRIX_FIELD_256
|
||||||
} // x^8 + x^5 + x^3 + x^2 + 1
|
} // x^8 + x^5 + x^3 + x^2 + 1
|
||||||
|
// PredefinedGenericGF::PDF417 => &PDF_417_FIELD,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ impl ReedSolomonDecoder {
|
|||||||
* @param twoS number of error-correction codewords available
|
* @param twoS number of error-correction codewords available
|
||||||
* @throws ReedSolomonException if decoding fails for any reason
|
* @throws ReedSolomonException if decoding fails for any reason
|
||||||
*/
|
*/
|
||||||
pub fn decode(&self, received: &mut Vec<i32>, twoS: i32) -> Result<(), Exceptions> {
|
pub fn decode(&self, received: &mut Vec<i32>, twoS: i32) -> Result<usize, Exceptions> {
|
||||||
let poly = GenericGFPoly::new(self.field, received).unwrap();
|
let poly = GenericGFPoly::new(self.field, received).unwrap();
|
||||||
let mut syndromeCoefficients = vec![0; twoS as usize];
|
let mut syndromeCoefficients = vec![0; twoS as usize];
|
||||||
let mut noError = true;
|
let mut noError = true;
|
||||||
@@ -74,7 +74,7 @@ impl ReedSolomonDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if noError {
|
if noError {
|
||||||
return Ok(());
|
return Ok(0);
|
||||||
}
|
}
|
||||||
let syndrome = match GenericGFPoly::new(self.field, &syndromeCoefficients) {
|
let syndrome = match GenericGFPoly::new(self.field, &syndromeCoefficients) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
@@ -97,7 +97,7 @@ impl ReedSolomonDecoder {
|
|||||||
//for (int i = 0; i < errorLocations.length; i++) {
|
//for (int i = 0; i < errorLocations.length; i++) {
|
||||||
let log_value = self.field.log(errorLocations[i] as i32)?;
|
let log_value = self.field.log(errorLocations[i] as i32)?;
|
||||||
if log_value > received.len() as i32 - 1 {
|
if log_value > received.len() as i32 - 1 {
|
||||||
return Ok(());
|
return Ok(0);
|
||||||
}
|
}
|
||||||
let position: isize = received.len() as isize - 1 - log_value as isize;
|
let position: isize = received.len() as isize - 1 - log_value as isize;
|
||||||
if position < 0 {
|
if position < 0 {
|
||||||
@@ -108,7 +108,7 @@ impl ReedSolomonDecoder {
|
|||||||
received[position as usize] =
|
received[position as usize] =
|
||||||
GenericGF::addOrSubtract(received[position as usize], errorMagnitudes[i]);
|
GenericGF::addOrSubtract(received[position as usize], errorMagnitudes[i]);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(errorLocations.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runEuclideanAlgorithm(
|
fn runEuclideanAlgorithm(
|
||||||
|
|||||||
@@ -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