Toolkit.UI.Controls.Avalonia
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
|
||||
namespace Gma.QrCodeNet.Encoding.ReedSolomon;
|
||||
|
||||
/// <summary>
|
||||
/// Description of GaloisField256.
|
||||
/// </summary>
|
||||
internal sealed class GaloisField256
|
||||
{
|
||||
internal GaloisField256(int primitive)
|
||||
{
|
||||
AntiLogTable = new int[256];
|
||||
LogTable = new int[256];
|
||||
|
||||
Primitive = primitive;
|
||||
|
||||
int gfx = 1;
|
||||
|
||||
// Power cycle is from 0 to 254. 2^255 = 1 = 2^0
|
||||
// Value cycle is from 1 to 255. Thus there should not have Log(0).
|
||||
for (int powers = 0; powers < 256; powers++)
|
||||
{
|
||||
AntiLogTable[powers] = gfx;
|
||||
if (powers != 255)
|
||||
{
|
||||
LogTable[gfx] = powers;
|
||||
}
|
||||
|
||||
gfx <<= 1; // gfx = gfx * 2 where alpha is 2.
|
||||
|
||||
if (gfx > 255)
|
||||
{
|
||||
gfx ^= primitive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int[] AntiLogTable { get; }
|
||||
private int[] LogTable { get; }
|
||||
|
||||
internal int Primitive { get; }
|
||||
|
||||
internal static GaloisField256 QRCodeGaloisField => new(QRCodeConstantVariable.QRCodePrimitive);
|
||||
|
||||
/// <returns>
|
||||
/// Powers of a in GF table. Where a = 2
|
||||
/// </returns>
|
||||
internal int Exponent(int powersOfa) => AntiLogTable[powersOfa];
|
||||
|
||||
/// <returns>
|
||||
/// Log (power of a) in GF table. Where a = 2
|
||||
/// </returns>
|
||||
internal int Log(int gfValue)
|
||||
{
|
||||
if (gfValue == 0)
|
||||
{
|
||||
throw new ArgumentException("GaloisField value will not be equal to 0, Log method.");
|
||||
}
|
||||
|
||||
return LogTable[gfValue];
|
||||
}
|
||||
|
||||
internal int Inverse(int gfValue)
|
||||
{
|
||||
if (gfValue == 0)
|
||||
{
|
||||
throw new ArgumentException("GaloisField value will not be equal to 0, Inverse method.");
|
||||
}
|
||||
|
||||
return Exponent(255 - Log(gfValue));
|
||||
}
|
||||
|
||||
internal int Addition(int gfValueA, int gfValueB) => gfValueA ^ gfValueB;
|
||||
|
||||
internal int Subtraction(int gfValueA, int gfValueB) => Addition(gfValueA, gfValueB); // Subtraction is same as addition.
|
||||
|
||||
/// <returns>
|
||||
/// Product of two values.
|
||||
/// In other words. a multiply b
|
||||
/// </returns>
|
||||
internal int Product(int gfValueA, int gfValueB)
|
||||
{
|
||||
if (gfValueA == 0 || gfValueB == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (gfValueA == 1)
|
||||
{
|
||||
return gfValueB;
|
||||
}
|
||||
if (gfValueB == 1)
|
||||
{
|
||||
return gfValueA;
|
||||
}
|
||||
|
||||
return Exponent((Log(gfValueA) + Log(gfValueB)) % 255);
|
||||
}
|
||||
|
||||
/// <returns>
|
||||
/// Quotient of two values.
|
||||
/// In other words. a divided b
|
||||
/// </returns>
|
||||
internal int Quotient(int gfValueA, int gfValueB)
|
||||
{
|
||||
if (gfValueA == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (gfValueB == 0)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(gfValueB)} cannot be zero.");
|
||||
}
|
||||
|
||||
if (gfValueB == 1)
|
||||
{
|
||||
return gfValueA;
|
||||
}
|
||||
|
||||
return Exponent(Math.Abs(Log(gfValueA) - Log(gfValueB)) % 255);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Gma.QrCodeNet.Encoding.ReedSolomon;
|
||||
|
||||
/// <summary>
|
||||
/// Description of GeneratorPolynomial.
|
||||
/// </summary>
|
||||
internal sealed class GeneratorPolynomial
|
||||
{
|
||||
/// <summary>
|
||||
/// After create GeneratorPolynomial. Keep it as long as possible.
|
||||
/// Unless QRCode encode is done or no more QRCode need to generate.
|
||||
/// </summary>
|
||||
internal GeneratorPolynomial(GaloisField256 gfield)
|
||||
{
|
||||
Gfield = gfield;
|
||||
CacheGenerator = new List<Polynomial>(10)
|
||||
{
|
||||
new Polynomial(Gfield, new int[] { 1 })
|
||||
};
|
||||
}
|
||||
|
||||
private GaloisField256 Gfield { get; }
|
||||
|
||||
private List<Polynomial> CacheGenerator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get generator by degree. (Largest degree for that generator)
|
||||
/// </summary>
|
||||
/// <returns>Generator</returns>
|
||||
internal Polynomial GetGenerator(int degree)
|
||||
{
|
||||
if (degree >= CacheGenerator.Count)
|
||||
{
|
||||
BuildGenerator(degree);
|
||||
}
|
||||
|
||||
return CacheGenerator[degree];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build Generator if we cannot find specific degree of generator from cache
|
||||
/// </summary>
|
||||
private void BuildGenerator(int degree)
|
||||
{
|
||||
lock (CacheGenerator)
|
||||
{
|
||||
int currentCacheLength = CacheGenerator.Count;
|
||||
if (degree >= currentCacheLength)
|
||||
{
|
||||
Polynomial lastGenerator = CacheGenerator[currentCacheLength - 1];
|
||||
|
||||
for (int d = currentCacheLength; d <= degree; d++)
|
||||
{
|
||||
Polynomial nextGenerator = lastGenerator.Multiply(new Polynomial(Gfield, new int[] { 1, Gfield.Exponent(d - 1) }));
|
||||
CacheGenerator.Add(nextGenerator);
|
||||
lastGenerator = nextGenerator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Gma.QrCodeNet.Encoding.ReedSolomon;
|
||||
|
||||
internal struct PolyDivideStruct
|
||||
{
|
||||
internal PolyDivideStruct(Polynomial quotient, Polynomial remainder)
|
||||
: this()
|
||||
{
|
||||
Quotient = quotient;
|
||||
Remainder = remainder;
|
||||
}
|
||||
|
||||
internal Polynomial Quotient { get; private set; }
|
||||
|
||||
internal Polynomial Remainder { get; private set; }
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
|
||||
namespace Gma.QrCodeNet.Encoding.ReedSolomon;
|
||||
|
||||
internal sealed class ReedSolomonEncoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Encode an array of data codeword with GaloisField 256.
|
||||
/// </summary>
|
||||
/// <param name="dataBytes">Array of data codewords for a single block.</param>
|
||||
/// <param name="numECBytes">Number of error correction codewords for data codewords</param>
|
||||
/// <param name="generatorPoly">Cached or newly create GeneratorPolynomial</param>
|
||||
/// <returns>Return error correction codewords array</returns>
|
||||
internal static byte[] Encode(byte[] dataBytes, int numECBytes, GeneratorPolynomial generatorPoly)
|
||||
{
|
||||
int dataLength = dataBytes.Length;
|
||||
if (generatorPoly == null)
|
||||
throw new ArgumentNullException(nameof(generatorPoly));
|
||||
|
||||
if (dataLength == 0)
|
||||
{
|
||||
throw new ArgumentException("There is no data bytes to encode.");
|
||||
}
|
||||
|
||||
if (numECBytes <= 0)
|
||||
{
|
||||
throw new ArgumentException("No Error Correction bytes.");
|
||||
}
|
||||
|
||||
int[] toEncode = ConvertToIntArray(dataBytes, dataLength, numECBytes);
|
||||
|
||||
Polynomial generator = generatorPoly.GetGenerator(numECBytes);
|
||||
|
||||
Polynomial dataPoly = new(generator.GField, toEncode);
|
||||
|
||||
PolyDivideStruct divideResult = dataPoly.Divide(generator);
|
||||
|
||||
int[] remainderCoeffs = divideResult.Remainder.Coefficients;
|
||||
|
||||
return ConvertTosByteArray(remainderCoeffs, numECBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert data codewords to int array. And add error correction space at end of that array
|
||||
/// </summary>
|
||||
/// <param name="dataBytes">Data codewords array</param>
|
||||
/// <param name="dataLength">Data codewords length</param>
|
||||
/// <param name="numECBytes">Num of error correction bytes</param>
|
||||
/// <returns>Int array for data codewords array follow by error correction space</returns>
|
||||
private static int[] ConvertToIntArray(byte[] dataBytes, int dataLength, int numECBytes)
|
||||
{
|
||||
int[] resultArray = new int[dataLength + numECBytes];
|
||||
|
||||
for (int index = 0; index < dataLength; index++)
|
||||
{
|
||||
resultArray[index] = dataBytes[index] & 0xff;
|
||||
}
|
||||
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reassembly error correction codewords. As Polynomial class will eliminate zero monomial at front.
|
||||
/// </summary>
|
||||
/// <param name="remainder">Remainder byte array after divide. </param>
|
||||
/// <param name="numECBytes">Error correction codewords length</param>
|
||||
/// <returns>Error correction codewords</returns>
|
||||
private static byte[] ConvertTosByteArray(int[] remainder, int numECBytes)
|
||||
{
|
||||
int remainderLength = remainder.Length;
|
||||
if (remainderLength > numECBytes)
|
||||
{
|
||||
throw new ArgumentException($"Num of {nameof(remainder)} bytes cannot be larger than {nameof(numECBytes)}.");
|
||||
}
|
||||
|
||||
int numZeroCoeffs = numECBytes - remainderLength;
|
||||
|
||||
byte[] resultArray = new byte[numECBytes];
|
||||
for (int index = 0; index < numZeroCoeffs; index++)
|
||||
{
|
||||
resultArray[index] = 0;
|
||||
}
|
||||
|
||||
for (int rIndex = 0; rIndex < remainderLength; rIndex++)
|
||||
{
|
||||
resultArray[numZeroCoeffs + rIndex] = (byte)remainder[rIndex];
|
||||
}
|
||||
|
||||
return resultArray;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user