243 lines
6.6 KiB
C#
243 lines
6.6 KiB
C#
using System;
|
|
|
|
namespace Gma.QrCodeNet.Encoding.ReedSolomon;
|
|
|
|
internal sealed class Polynomial
|
|
{
|
|
internal Polynomial(GaloisField256 gfield, int[] coefficients)
|
|
{
|
|
int coefficientsLength = coefficients.Length;
|
|
|
|
if (coefficientsLength == 0 || coefficients is null)
|
|
{
|
|
throw new ArithmeticException($"Cannot create empty {nameof(Polynomial)}.");
|
|
}
|
|
|
|
GField = gfield;
|
|
|
|
Primitive = gfield.Primitive;
|
|
|
|
if (coefficientsLength > 1 && coefficients[0] == 0)
|
|
{
|
|
int firstNonZeroIndex = 1;
|
|
while (firstNonZeroIndex < coefficientsLength && coefficients[firstNonZeroIndex] == 0)
|
|
{
|
|
firstNonZeroIndex++;
|
|
}
|
|
|
|
if (firstNonZeroIndex == coefficientsLength)
|
|
{
|
|
Coefficients = new int[] { 0 };
|
|
}
|
|
else
|
|
{
|
|
int newLength = coefficientsLength - firstNonZeroIndex;
|
|
Coefficients = new int[newLength];
|
|
Array.Copy(coefficients, firstNonZeroIndex, Coefficients, 0, newLength);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Coefficients = new int[coefficientsLength];
|
|
Array.Copy(coefficients, Coefficients, coefficientsLength);
|
|
}
|
|
}
|
|
|
|
internal int[] Coefficients { get; }
|
|
|
|
internal GaloisField256 GField { get; }
|
|
|
|
internal int Degree => Coefficients.Length - 1;
|
|
|
|
internal int Primitive { get; }
|
|
|
|
internal bool IsMonomialZero => Coefficients[0] == 0;
|
|
|
|
/// <returns>
|
|
/// Coefficient position. where (coefficient)x^degree
|
|
/// </returns>
|
|
internal int GetCoefficient(int degree)
|
|
{
|
|
// Eg: x^2 + x + 1. degree 1, reverse position = degree + 1 = 2.
|
|
// Pos = 3 - 2 = 1
|
|
return Coefficients[^(degree + 1)];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add another Polynomial to current one
|
|
/// </summary>
|
|
/// <param name="other">The polynomial need to add or subtract to current one</param>
|
|
/// <returns>Result polynomial after add or subtract</returns>
|
|
internal Polynomial AddOrSubtract(Polynomial other)
|
|
{
|
|
if (Primitive != other.Primitive)
|
|
{
|
|
throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(AddOrSubtract)} as they do not have the same {nameof(Primitive)}" +
|
|
$" for {nameof(GaloisField256)}.");
|
|
}
|
|
if (IsMonomialZero)
|
|
{
|
|
return other;
|
|
}
|
|
else if (other.IsMonomialZero)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
int otherLength = other.Coefficients.Length;
|
|
int thisLength = Coefficients.Length;
|
|
|
|
if (otherLength > thisLength)
|
|
{
|
|
return CoefficientXor(Coefficients, other.Coefficients);
|
|
}
|
|
else
|
|
{
|
|
return CoefficientXor(other.Coefficients, Coefficients);
|
|
}
|
|
}
|
|
|
|
internal Polynomial CoefficientXor(int[] smallerCoefficients, int[] largerCoefficients)
|
|
{
|
|
if (smallerCoefficients.Length > largerCoefficients.Length)
|
|
{
|
|
throw new ArgumentException($"Cannot perform {nameof(CoefficientXor)} method as smaller {nameof(Coefficients)} length is greater than the larger one.");
|
|
}
|
|
|
|
int targetLength = largerCoefficients.Length;
|
|
int[] xorCoefficient = new int[targetLength];
|
|
int lengthDiff = largerCoefficients.Length - smallerCoefficients.Length;
|
|
|
|
Array.Copy(largerCoefficients, 0, xorCoefficient, 0, lengthDiff);
|
|
|
|
for (int index = lengthDiff; index < targetLength; index++)
|
|
{
|
|
xorCoefficient[index] = GField.Addition(largerCoefficients[index], smallerCoefficients[index - lengthDiff]);
|
|
}
|
|
|
|
return new Polynomial(GField, xorCoefficient);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Multiply current Polynomial to another one.
|
|
/// </summary>
|
|
/// <returns>Result polynomial after multiply</returns>
|
|
internal Polynomial Multiply(Polynomial other)
|
|
{
|
|
if (Primitive != other.Primitive)
|
|
{
|
|
throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(Multiply)} as they do not have the same {nameof(Primitive)}" +
|
|
$" for {nameof(GaloisField256)}.");
|
|
}
|
|
if (IsMonomialZero || other.IsMonomialZero)
|
|
{
|
|
return new Polynomial(GField, new int[] { 0 });
|
|
}
|
|
|
|
int[] aCoefficients = Coefficients;
|
|
int aLength = aCoefficients.Length;
|
|
int[] bCoefficient = other.Coefficients;
|
|
int bLength = bCoefficient.Length;
|
|
int[] rCoefficients = new int[aLength + bLength - 1];
|
|
|
|
for (int aIndex = 0; aIndex < aLength; aIndex++)
|
|
{
|
|
int aCoeff = aCoefficients[aIndex];
|
|
for (int bIndex = 0; bIndex < bLength; bIndex++)
|
|
{
|
|
rCoefficients[aIndex + bIndex] =
|
|
GField.Addition(rCoefficients[aIndex + bIndex], GField.Product(aCoeff, bCoefficient[bIndex]));
|
|
}
|
|
}
|
|
return new Polynomial(GField, rCoefficients);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Multiplay scalar to current polynomial
|
|
/// </summary>
|
|
/// <returns>Result of polynomial after multiply scalar</returns>
|
|
internal Polynomial MultiplyScalar(int scalar)
|
|
{
|
|
if (scalar == 0)
|
|
{
|
|
return new Polynomial(GField, new int[] { 0 });
|
|
}
|
|
else if (scalar == 1)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
int length = Coefficients.Length;
|
|
int[] rCoefficient = new int[length];
|
|
|
|
for (int index = 0; index < length; index++)
|
|
{
|
|
rCoefficient[index] = GField.Product(Coefficients[index], scalar);
|
|
}
|
|
|
|
return new Polynomial(GField, rCoefficient);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Divide current polynomial by "other"
|
|
/// </summary>
|
|
/// <returns>Result polynomial after divide</returns>
|
|
internal PolyDivideStruct Divide(Polynomial other)
|
|
{
|
|
if (Primitive != other.Primitive)
|
|
{
|
|
throw new ArgumentException($"{nameof(Polynomial)} cannot perform {nameof(Divide)} as they do not have the same {nameof(Primitive)}" +
|
|
$" for {nameof(GaloisField256)}.");
|
|
}
|
|
if (other.IsMonomialZero)
|
|
{
|
|
throw new ArgumentException($"Cannot divide by {nameof(Polynomial)} Zero.");
|
|
}
|
|
|
|
// This divide by other = a divide by b
|
|
int aLength = Coefficients.Length;
|
|
|
|
// We will make change to aCoefficient. It will return as remainder
|
|
int[] aCoefficients = new int[aLength];
|
|
Array.Copy(Coefficients, 0, aCoefficients, 0, aLength);
|
|
|
|
int bLength = other.Coefficients.Length;
|
|
|
|
if (aLength < bLength)
|
|
{
|
|
return new PolyDivideStruct(new Polynomial(GField, new int[] { 0 }), this);
|
|
}
|
|
else
|
|
{
|
|
// Quotient coefficients
|
|
// qLastIndex = alength - blength qlength = qLastIndex + 1
|
|
int[] qCoefficients = new int[(aLength - bLength) + 1];
|
|
|
|
// Denominator
|
|
int otherLeadingTerm = other.GetCoefficient(other.Degree);
|
|
int inverseOtherLeadingTerm = GField.Inverse(otherLeadingTerm);
|
|
|
|
for (int aIndex = 0; aIndex <= aLength - bLength; aIndex++)
|
|
{
|
|
if (aCoefficients[aIndex] != 0)
|
|
{
|
|
int aScalar = GField.Product(inverseOtherLeadingTerm, aCoefficients[aIndex]);
|
|
Polynomial term = other.MultiplyScalar(aScalar);
|
|
qCoefficients[aIndex] = aScalar;
|
|
|
|
int[] bCoefficient = term.Coefficients;
|
|
if (bCoefficient[0] != 0)
|
|
{
|
|
for (int bIndex = 0; bIndex < bLength; bIndex++)
|
|
{
|
|
aCoefficients[aIndex + bIndex] = GField.Subtraction(aCoefficients[aIndex + bIndex], bCoefficient[bIndex]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return new PolyDivideStruct(new Polynomial(GField, qCoefficients), new Polynomial(GField, aCoefficients));
|
|
}
|
|
}
|
|
}
|