Toolkit.UI.Controls.Avalonia

This commit is contained in:
TheXamlGuy
2024-04-13 11:41:33 +01:00
parent 62a7e94e19
commit 862e7b2e34
97 changed files with 8558 additions and 0 deletions
@@ -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;
}
}