You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
9.8 KiB
289 lines
9.8 KiB
<?php
|
|
/*
|
|
* Copyright 2007 ZXing authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
namespace Zxing\Common\Reedsolomon;
|
|
|
|
/**
|
|
* <p>Represents a polynomial whose coefficients are elements of a GF.
|
|
* Instances of this class are immutable.</p>
|
|
*
|
|
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
|
* port of his C++ Reed-Solomon implementation.</p>
|
|
*
|
|
* @author Sean Owen
|
|
*/
|
|
final class GenericGFPoly
|
|
{
|
|
|
|
private $field;
|
|
private $coefficients;
|
|
|
|
/**
|
|
* @param field the {@link GenericGF} instance representing the field to use
|
|
* to perform computations
|
|
* @param coefficients array coefficients as ints representing elements of GF(size), arranged
|
|
* from most significant (highest-power term) coefficient to least significant
|
|
*
|
|
* @throws InvalidArgumentException if argument is null or empty,
|
|
* or if leading coefficient is 0 and this is not a
|
|
* constant polynomial (that is, it is not the monomial "0")
|
|
*/
|
|
public function __construct($field, $coefficients)
|
|
{
|
|
if (count($coefficients) == 0) {
|
|
throw new \InvalidArgumentException();
|
|
}
|
|
$this->field = $field;
|
|
$coefficientsLength = count($coefficients);
|
|
if ($coefficientsLength > 1 && $coefficients[0] == 0) {
|
|
// Leading term must be non-zero for anything except the constant polynomial "0"
|
|
$firstNonZero = 1;
|
|
while ($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] == 0) {
|
|
$firstNonZero++;
|
|
}
|
|
if ($firstNonZero == $coefficientsLength) {
|
|
$this->coefficients = [0];
|
|
} else {
|
|
$this->coefficients = fill_array(0, $coefficientsLength - $firstNonZero, 0);
|
|
$this->coefficients = arraycopy($coefficients,
|
|
$firstNonZero,
|
|
$this->coefficients,
|
|
0,
|
|
count($this->coefficients));
|
|
}
|
|
} else {
|
|
$this->coefficients = $coefficients;
|
|
}
|
|
}
|
|
|
|
public function getCoefficients()
|
|
{
|
|
return $this->coefficients;
|
|
}
|
|
|
|
/**
|
|
* @return evaluation of this polynomial at a given point
|
|
*/
|
|
public function evaluateAt($a)
|
|
{
|
|
if ($a == 0) {
|
|
// Just return the x^0 coefficient
|
|
return $this->getCoefficient(0);
|
|
}
|
|
$size = count($this->coefficients);
|
|
if ($a == 1) {
|
|
// Just the sum of the coefficients
|
|
$result = 0;
|
|
foreach ($this->coefficients as $coefficient) {
|
|
$result = GenericGF::addOrSubtract($result, $coefficient);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
$result = $this->coefficients[0];
|
|
for ($i = 1; $i < $size; $i++) {
|
|
$result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return coefficient of x^degree term in this polynomial
|
|
*/
|
|
public function getCoefficient($degree)
|
|
{
|
|
return $this->coefficients[count($this->coefficients) - 1 - $degree];
|
|
}
|
|
|
|
public function multiply($other)
|
|
{
|
|
if (is_int($other)) {
|
|
return $this->multiply_($other);
|
|
}
|
|
if ($this->field !== $other->field) {
|
|
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
|
}
|
|
if ($this->isZero() || $other->isZero()) {
|
|
return $this->field->getZero();
|
|
}
|
|
$aCoefficients = $this->coefficients;
|
|
$aLength = count($aCoefficients);
|
|
$bCoefficients = $other->coefficients;
|
|
$bLength = count($bCoefficients);
|
|
$product = fill_array(0, $aLength + $bLength - 1, 0);
|
|
for ($i = 0; $i < $aLength; $i++) {
|
|
$aCoeff = $aCoefficients[$i];
|
|
for ($j = 0; $j < $bLength; $j++) {
|
|
$product[$i + $j] = GenericGF::addOrSubtract($product[$i + $j],
|
|
$this->field->multiply($aCoeff, $bCoefficients[$j]));
|
|
}
|
|
}
|
|
|
|
return new GenericGFPoly($this->field, $product);
|
|
}
|
|
|
|
public function multiply_($scalar)
|
|
{
|
|
if ($scalar == 0) {
|
|
return $this->field->getZero();
|
|
}
|
|
if ($scalar == 1) {
|
|
return $this;
|
|
}
|
|
$size = count($this->coefficients);
|
|
$product = fill_array(0, $size, 0);
|
|
for ($i = 0; $i < $size; $i++) {
|
|
$product[$i] = $this->field->multiply($this->coefficients[$i], $scalar);
|
|
}
|
|
|
|
return new GenericGFPoly($this->field, $product);
|
|
}
|
|
|
|
/**
|
|
* @return true iff this polynomial is the monomial "0"
|
|
*/
|
|
public function isZero()
|
|
{
|
|
return $this->coefficients[0] == 0;
|
|
}
|
|
|
|
public function multiplyByMonomial($degree, $coefficient)
|
|
{
|
|
if ($degree < 0) {
|
|
throw new \InvalidArgumentException();
|
|
}
|
|
if ($coefficient == 0) {
|
|
return $this->field->getZero();
|
|
}
|
|
$size = count($this->coefficients);
|
|
$product = fill_array(0, $size + $degree, 0);
|
|
for ($i = 0; $i < $size; $i++) {
|
|
$product[$i] = $this->field->multiply($this->coefficients[$i], $coefficient);
|
|
}
|
|
|
|
return new GenericGFPoly($this->field, $product);
|
|
}
|
|
|
|
public function divide($other)
|
|
{
|
|
if ($this->field !== $other->field) {
|
|
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
|
}
|
|
if ($other->isZero()) {
|
|
throw new \InvalidArgumentException("Divide by 0");
|
|
}
|
|
|
|
$quotient = $this->field->getZero();
|
|
$remainder = $this;
|
|
|
|
$denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
|
|
$inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm);
|
|
|
|
while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) {
|
|
$degreeDifference = $remainder->getDegree() - $other->getDegree();
|
|
$scale = $this->field->multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
|
|
$term = $other->multiplyByMonomial($degreeDifference, $scale);
|
|
$iterationQuotient = $this->field->buildMonomial($degreeDifference, $scale);
|
|
$quotient = $quotient->addOrSubtract($iterationQuotient);
|
|
$remainder = $remainder->addOrSubtract($term);
|
|
}
|
|
|
|
return [$quotient, $remainder];
|
|
}
|
|
|
|
/**
|
|
* @return degree of this polynomial
|
|
*/
|
|
public function getDegree()
|
|
{
|
|
return count($this->coefficients) - 1;
|
|
}
|
|
|
|
public function addOrSubtract($other)
|
|
{
|
|
if ($this->field !== $other->field) {
|
|
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
|
}
|
|
if ($this->isZero()) {
|
|
return $other;
|
|
}
|
|
if ($other->isZero()) {
|
|
return $this;
|
|
}
|
|
|
|
$smallerCoefficients = $this->coefficients;
|
|
$largerCoefficients = $other->coefficients;
|
|
if (count($smallerCoefficients) > count($largerCoefficients)) {
|
|
$temp = $smallerCoefficients;
|
|
$smallerCoefficients = $largerCoefficients;
|
|
$largerCoefficients = $temp;
|
|
}
|
|
$sumDiff = fill_array(0, count($largerCoefficients), 0);
|
|
$lengthDiff = count($largerCoefficients) - count($smallerCoefficients);
|
|
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
|
$sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff);
|
|
|
|
$countLargerCoefficients = count($largerCoefficients);
|
|
for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) {
|
|
$sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
|
|
}
|
|
|
|
return new GenericGFPoly($this->field, $sumDiff);
|
|
}
|
|
|
|
//@Override
|
|
|
|
public function toString()
|
|
{
|
|
$result = '';
|
|
for ($degree = $this->getDegree(); $degree >= 0; $degree--) {
|
|
$coefficient = $this->getCoefficient($degree);
|
|
if ($coefficient != 0) {
|
|
if ($coefficient < 0) {
|
|
$result .= " - ";
|
|
$coefficient = -$coefficient;
|
|
} else {
|
|
if (strlen($result) > 0) {
|
|
$result .= " + ";
|
|
}
|
|
}
|
|
if ($degree == 0 || $coefficient != 1) {
|
|
$alphaPower = $this->field->log($coefficient);
|
|
if ($alphaPower == 0) {
|
|
$result .= '1';
|
|
} else if ($alphaPower == 1) {
|
|
$result .= 'a';
|
|
} else {
|
|
$result .= "a^";
|
|
$result .= ($alphaPower);
|
|
}
|
|
}
|
|
if ($degree != 0) {
|
|
if ($degree == 1) {
|
|
$result .= 'x';
|
|
} else {
|
|
$result .= "x^";
|
|
$result .= $degree;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
}
|
|
|