From 833a0fb123786901988a3c18165369d3fc138fad Mon Sep 17 00:00:00 2001 From: Kalafatis Kwstas Date: Mon, 21 Oct 2024 21:53:18 +0300 Subject: [PATCH 1/2] Update StyleCop (#480) --- .../ExtendedEuclideanAlgorithmTest.cs | 12 +- Algorithms/Algorithms.csproj | 2 +- .../BurrowsWheelerTransform.cs | 2 +- .../DataCompression/HuffmanCompressor.cs | 4 +- .../DataCompression/ShannonFanoCompressor.cs | 18 +- Algorithms/Encoders/NysiisEncoder.cs | 16 +- Algorithms/Graph/FloydWarshall.cs | 2 +- .../Eigenvalue/PowerIteration.cs | 6 +- .../ChineseRemainderTheorem.cs | 8 +- .../ExtendedEuclideanAlgorithm.cs | 20 +- .../ModularMultiplicativeInverse.cs | 8 +- Algorithms/Other/FloodFill.cs | 34 +-- Algorithms/Other/GaussOptimization.cs | 2 +- Algorithms/Other/RGBHSVConversion.cs | 6 +- Algorithms/Search/FastSearcher.cs | 4 +- Algorithms/Sorters/Comparison/MergeSorter.cs | 2 +- Algorithms/Sorters/Comparison/TimSorter.cs | 247 +++++++++--------- .../Sorters/Comparison/TimSorterSettings.cs | 14 + Algorithms/Sorters/Utils/GallopingStrategy.cs | 4 +- .../Strings/Similarity/CosineSimilarity.cs | 6 +- .../Similarity/DamerauLevenshteinDistance.cs | 2 +- DataStructures/AATree/AATree.cs | 2 +- DataStructures/DataStructures.csproj | 2 +- DataStructures/Graph/DirectedWeightedGraph.cs | 2 +- DataStructures/Hashing/HashTable.cs | 2 +- .../RedBlackTree/RedBlackTreeNode.cs | 4 +- DataStructures/ScapegoatTree/ScapegoatTree.cs | 2 +- Utilities/Extensions/DictionaryExtensions.cs | 2 +- Utilities/Utilities.csproj | 2 +- 29 files changed, 219 insertions(+), 218 deletions(-) create mode 100644 Algorithms/Sorters/Comparison/TimSorterSettings.cs diff --git a/Algorithms.Tests/ModularArithmetic/ExtendedEuclideanAlgorithmTest.cs b/Algorithms.Tests/ModularArithmetic/ExtendedEuclideanAlgorithmTest.cs index ea3e7792..83ab1caf 100644 --- a/Algorithms.Tests/ModularArithmetic/ExtendedEuclideanAlgorithmTest.cs +++ b/Algorithms.Tests/ModularArithmetic/ExtendedEuclideanAlgorithmTest.cs @@ -23,9 +23,9 @@ public static void TestCompute(long a, long b, long expectedGCD, long expectedBe var eeaResult = ExtendedEuclideanAlgorithm.Compute(a, b); // Assert - Assert.That(eeaResult.gcd, Is.EqualTo(expectedGCD)); - Assert.That(eeaResult.bezoutA, Is.EqualTo(expectedBezoutOfA)); - Assert.That(eeaResult.bezoutB, Is.EqualTo(expectedBezoutOfB)); + Assert.That(eeaResult.Gcd, Is.EqualTo(expectedGCD)); + Assert.That(eeaResult.BezoutA, Is.EqualTo(expectedBezoutOfA)); + Assert.That(eeaResult.BezoutB, Is.EqualTo(expectedBezoutOfB)); } [TestCase(240, 46, 2, -9, 47)] @@ -45,8 +45,8 @@ public static void TestCompute_BigInteger(long a, long b, long expectedGCD, long var eeaResult = ExtendedEuclideanAlgorithm.Compute(new BigInteger(a), new BigInteger(b)); // Assert - Assert.That(eeaResult.gcd, Is.EqualTo(new BigInteger(expectedGCD))); - Assert.That(eeaResult.bezoutA, Is.EqualTo(new BigInteger(expectedBezoutOfA))); - Assert.That(eeaResult.bezoutB, Is.EqualTo(new BigInteger(expectedBezoutOfB))); + Assert.That(eeaResult.Gcd, Is.EqualTo(new BigInteger(expectedGCD))); + Assert.That(eeaResult.BezoutA, Is.EqualTo(new BigInteger(expectedBezoutOfA))); + Assert.That(eeaResult.BezoutB, Is.EqualTo(new BigInteger(expectedBezoutOfB))); } } diff --git a/Algorithms/Algorithms.csproj b/Algorithms/Algorithms.csproj index 84283140..671d3e70 100644 --- a/Algorithms/Algorithms.csproj +++ b/Algorithms/Algorithms.csproj @@ -18,7 +18,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Algorithms/DataCompression/BurrowsWheelerTransform.cs b/Algorithms/DataCompression/BurrowsWheelerTransform.cs index e84aab8d..6ec19387 100644 --- a/Algorithms/DataCompression/BurrowsWheelerTransform.cs +++ b/Algorithms/DataCompression/BurrowsWheelerTransform.cs @@ -16,7 +16,7 @@ public class BurrowsWheelerTransform /// rotation matrix. /// /// Input string. - public (string encoded, int index) Encode(string s) + public (string Encoded, int Index) Encode(string s) { if (s.Length == 0) { diff --git a/Algorithms/DataCompression/HuffmanCompressor.cs b/Algorithms/DataCompression/HuffmanCompressor.cs index a9d3f689..368f0704 100644 --- a/Algorithms/DataCompression/HuffmanCompressor.cs +++ b/Algorithms/DataCompression/HuffmanCompressor.cs @@ -27,7 +27,7 @@ public HuffmanCompressor(IComparisonSorter sorter, Translator translat /// /// Text message to compress. /// Compressed string and keys to decompress it. - public (string compressedText, Dictionary decompressionKeys) Compress(string uncompressedText) + public (string CompressedText, Dictionary DecompressionKeys) Compress(string uncompressedText) { if (string.IsNullOrEmpty(uncompressedText)) { @@ -70,7 +70,7 @@ private static ListNode[] GetListNodesFromText(string text) return occurenceCounts.Select(kvp => new ListNode(kvp.Key, 1d * kvp.Value / text.Length)).ToArray(); } - private (Dictionary compressionKeys, Dictionary decompressionKeys) GetKeys( + private (Dictionary CompressionKeys, Dictionary DecompressionKeys) GetKeys( ListNode tree) { var compressionKeys = new Dictionary(); diff --git a/Algorithms/DataCompression/ShannonFanoCompressor.cs b/Algorithms/DataCompression/ShannonFanoCompressor.cs index 6a48e7b4..3aba6761 100644 --- a/Algorithms/DataCompression/ShannonFanoCompressor.cs +++ b/Algorithms/DataCompression/ShannonFanoCompressor.cs @@ -10,11 +10,11 @@ namespace Algorithms.DataCompression; /// public class ShannonFanoCompressor { - private readonly IHeuristicKnapsackSolver<(char symbol, double frequency)> splitter; + private readonly IHeuristicKnapsackSolver<(char Symbol, double Frequency)> splitter; private readonly Translator translator; public ShannonFanoCompressor( - IHeuristicKnapsackSolver<(char symbol, double frequency)> splitter, + IHeuristicKnapsackSolver<(char Symbol, double Frequency)> splitter, Translator translator) { this.splitter = splitter; @@ -27,7 +27,7 @@ public ShannonFanoCompressor( /// /// Text message to compress. /// Compressed string and keys to decompress it. - public (string compressedText, Dictionary decompressionKeys) Compress(string uncompressedText) + public (string CompressedText, Dictionary DecompressionKeys) Compress(string uncompressedText) { if (string.IsNullOrEmpty(uncompressedText)) { @@ -49,7 +49,7 @@ public ShannonFanoCompressor( return (translator.Translate(uncompressedText, compressionKeys), decompressionKeys); } - private (Dictionary compressionKeys, Dictionary decompressionKeys) GetKeys( + private (Dictionary CompressionKeys, Dictionary DecompressionKeys) GetKeys( ListNode tree) { var compressionKeys = new Dictionary(); @@ -57,8 +57,8 @@ public ShannonFanoCompressor( if (tree.Data.Length == 1) { - compressionKeys.Add(tree.Data[0].symbol.ToString(), string.Empty); - decompressionKeys.Add(string.Empty, tree.Data[0].symbol.ToString()); + compressionKeys.Add(tree.Data[0].Symbol.ToString(), string.Empty); + decompressionKeys.Add(string.Empty, tree.Data[0].Symbol.ToString()); return (compressionKeys, decompressionKeys); } @@ -86,7 +86,7 @@ private ListNode GenerateShannonFanoTree(ListNode node) return node; } - var left = splitter.Solve(node.Data, 0.5 * node.Data.Sum(x => x.frequency), x => x.frequency, _ => 1); + var left = splitter.Solve(node.Data, 0.5 * node.Data.Sum(x => x.Frequency), x => x.Frequency, _ => 1); var right = node.Data.Except(left).ToArray(); node.LeftChild = GenerateShannonFanoTree(new ListNode(left)); @@ -122,9 +122,9 @@ private ListNode GetListNodeFromText(string text) /// public class ListNode { - public ListNode((char symbol, double frequency)[] data) => Data = data; + public ListNode((char Symbol, double Frequency)[] data) => Data = data; - public (char symbol, double frequency)[] Data { get; } + public (char Symbol, double Frequency)[] Data { get; } public ListNode? RightChild { get; set; } diff --git a/Algorithms/Encoders/NysiisEncoder.cs b/Algorithms/Encoders/NysiisEncoder.cs index a5e98747..10810af3 100644 --- a/Algorithms/Encoders/NysiisEncoder.cs +++ b/Algorithms/Encoders/NysiisEncoder.cs @@ -51,13 +51,13 @@ private string RemoveDuplicates(string text) private string TrimEnd(string text) { - var checks = new (string from, string to)?[] + var checks = new (string From, string To)?[] { ("S", string.Empty), ("AY", "Y"), ("A", string.Empty), }; - var replacement = checks.FirstOrDefault(t => text.EndsWith(t!.Value.from)); + var replacement = checks.FirstOrDefault(t => text.EndsWith(t!.Value.From)); if (replacement is { }) { var (from, to) = replacement!.Value; @@ -69,7 +69,7 @@ private string TrimEnd(string text) private string ReplaceStep(string text, int i) { - (string from, string to)[] replacements = + (string From, string To)[] replacements = { ("EV", "AF"), ("E", "A"), @@ -134,7 +134,7 @@ private bool TryReplace(string text, int index, (string, string)[] opts, out str private string StartReplace(string start) { - var checks = new (string from, string to)?[] + var checks = new (string From, string To)?[] { ("MAC", "MCC"), ("KN", "NN"), @@ -143,7 +143,7 @@ private string StartReplace(string start) ("PF", "FF"), ("SCH", "SSS"), }; - var replacement = checks.FirstOrDefault(t => start.StartsWith(t!.Value.from)); + var replacement = checks.FirstOrDefault(t => start.StartsWith(t!.Value.From)); if (replacement is { }) { var (from, to) = replacement!.Value; @@ -155,7 +155,7 @@ private string StartReplace(string start) private string EndReplace(string end) { - var checks = new (string from, string to)?[] + var checks = new (string From, string To)?[] { ("EE", "Y"), ("IE", "Y"), @@ -164,7 +164,7 @@ private string EndReplace(string end) ("NT", "D"), ("ND", "D"), }; - var replacement = checks.FirstOrDefault(t => end.EndsWith(t!.Value.from)); + var replacement = checks.FirstOrDefault(t => end.EndsWith(t!.Value.From)); if (replacement is { }) { var (from, to) = replacement!.Value; @@ -175,5 +175,5 @@ private string EndReplace(string end) } private string Replace(string text, int index, int length, string substitute) => - text[..index] + substitute + text[(index + length) ..]; + text[..index] + substitute + text[(index + length)..]; } diff --git a/Algorithms/Graph/FloydWarshall.cs b/Algorithms/Graph/FloydWarshall.cs index 7bb9d707..8c7fc466 100644 --- a/Algorithms/Graph/FloydWarshall.cs +++ b/Algorithms/Graph/FloydWarshall.cs @@ -49,7 +49,7 @@ public class FloydWarshall { for (var j = 0; j < distances.GetLength(0); j++) { - var dist = graph.AdjacentDistance(graph.Vertices[i] !, graph.Vertices[j] !); + var dist = graph.AdjacentDistance(graph.Vertices[i]!, graph.Vertices[j]!); distances[i, j] = dist != 0 ? dist : double.PositiveInfinity; } } diff --git a/Algorithms/LinearAlgebra/Eigenvalue/PowerIteration.cs b/Algorithms/LinearAlgebra/Eigenvalue/PowerIteration.cs index 8e503992..df225723 100644 --- a/Algorithms/LinearAlgebra/Eigenvalue/PowerIteration.cs +++ b/Algorithms/LinearAlgebra/Eigenvalue/PowerIteration.cs @@ -27,7 +27,7 @@ public static class PowerIteration /// Dominant eigenvalue and eigenvector pair. /// The matrix is not square-shaped. /// The length of the start vector doesn't equal the size of the source matrix. - public static (double eigenvalue, double[] eigenvector) Dominant( + public static (double Eigenvalue, double[] Eigenvector) Dominant( double[,] source, double[] startVector, double error = 0.00001) @@ -61,7 +61,7 @@ public static (double eigenvalue, double[] eigenvector) Dominant( var eigenvalue = source.Multiply(currentEigenVector.ToColumnVector()).ToRowVector().Magnitude(); - return (eigenvalue, eigenvector: currentEigenVector); + return (eigenvalue, Eigenvector: currentEigenVector); } /// @@ -81,6 +81,6 @@ public static (double eigenvalue, double[] eigenvector) Dominant( /// Dominant eigenvalue and eigenvector pair. /// The matrix is not square-shaped. /// The length of the start vector doesn't equal the size of the source matrix. - public static (double eigenvalue, double[] eigenvector) Dominant(double[,] source, double error = 0.00001) => + public static (double Eigenvalue, double[] Eigenvector) Dominant(double[,] source, double error = 0.00001) => Dominant(source, new Random().NextVector(source.GetLength(1)), error); } diff --git a/Algorithms/ModularArithmetic/ChineseRemainderTheorem.cs b/Algorithms/ModularArithmetic/ChineseRemainderTheorem.cs index 1eeaef76..9f7d88cb 100644 --- a/Algorithms/ModularArithmetic/ChineseRemainderTheorem.cs +++ b/Algorithms/ModularArithmetic/ChineseRemainderTheorem.cs @@ -49,7 +49,7 @@ public static long Compute(List listOfAs, List listOfNs) var n_i = listOfNs[i]; var modulus_i = prodN / n_i; - var bezout_modulus_i = ExtendedEuclideanAlgorithm.Compute(n_i, modulus_i).bezoutB; + var bezout_modulus_i = ExtendedEuclideanAlgorithm.Compute(n_i, modulus_i).BezoutB; result += a_i * bezout_modulus_i * modulus_i; } @@ -102,7 +102,7 @@ public static BigInteger Compute(List listOfAs, List lis var n_i = listOfNs[i]; var modulus_i = prodN / n_i; - var bezout_modulus_i = ExtendedEuclideanAlgorithm.Compute(n_i, modulus_i).bezoutB; + var bezout_modulus_i = ExtendedEuclideanAlgorithm.Compute(n_i, modulus_i).BezoutB; result += a_i * bezout_modulus_i * modulus_i; } @@ -145,7 +145,7 @@ private static void CheckRequirements(List listOfAs, List listOfNs) for (var j = i + 1; j < listOfNs.Count; j++) { long gcd; - if ((gcd = ExtendedEuclideanAlgorithm.Compute(listOfNs[i], listOfNs[j]).gcd) != 1L) + if ((gcd = ExtendedEuclideanAlgorithm.Compute(listOfNs[i], listOfNs[j]).Gcd) != 1L) { throw new ArgumentException($"The GCD of n_{i} = {listOfNs[i]} and n_{j} = {listOfNs[j]} equals {gcd} and thus these values aren't coprime."); } @@ -182,7 +182,7 @@ private static void CheckRequirements(List listOfAs, List - /// Computes the greatest common divisor (gcd) of integers a and b, also the coefficients of Bézout's identity, - /// which are integers x and y such that a*bezoutCoefficientOfA + b*bezoutCoefficientOfB = gcd(a, b). + /// Computes the greatest common divisor (Gcd) of integers a and b, also the coefficients of Bézout's identity, + /// which are integers x and y such that a*bezoutCoefficientOfA + b*bezoutCoefficientOfB = Gcd(a, b). /// /// Input number. /// Second input number. - /// A record of ExtendedEuclideanAlgorithmResult containing the bezout coefficients of a and b as well as the gcd(a,b). + /// A record of ExtendedEuclideanAlgorithmResult containing the bezout coefficients of a and b as well as the Gcd(a,b). public static ExtendedEuclideanAlgorithmResult Compute(long a, long b) { long quotient; @@ -46,12 +46,12 @@ public static ExtendedEuclideanAlgorithmResult Compute(long a, long b) } /// - /// Computes the greatest common divisor (gcd) of integers a and b, also the coefficients of Bézout's identity, - /// which are integers x and y such that a*bezoutCoefficientOfA + b*bezoutCoefficientOfB = gcd(a, b). + /// Computes the greatest common divisor (Gcd) of integers a and b, also the coefficients of Bézout's identity, + /// which are integers x and y such that a*bezoutCoefficientOfA + b*bezoutCoefficientOfB = Gcd(a, b). /// /// Input number. /// Second input number. - /// A record of ExtendedEuclideanAlgorithmResult containing the bezout coefficients of a and b as well as the gcd(a,b). + /// A record of ExtendedEuclideanAlgorithmResult containing the bezout coefficients of a and b as well as the Gcd(a,b). public static ExtendedEuclideanAlgorithmResult Compute(BigInteger a, BigInteger b) { BigInteger quotient; @@ -87,8 +87,8 @@ public static ExtendedEuclideanAlgorithmResult Compute(BigInteger a, /// The result type for the computation of the Extended Euclidean Algorithm. /// /// The data type of the computation (i.e. long or BigInteger). - /// The bezout coefficient of the parameter a to the computation. - /// The bezout coefficient of the parameter b to the computation. - /// The greatest common divisor of the parameters a and b to the computation. - public record ExtendedEuclideanAlgorithmResult(T bezoutA, T bezoutB, T gcd); + /// The bezout coefficient of the parameter a to the computation. + /// The bezout coefficient of the parameter b to the computation. + /// The greatest common divisor of the parameters a and b to the computation. + public record ExtendedEuclideanAlgorithmResult(T BezoutA, T BezoutB, T Gcd); } diff --git a/Algorithms/ModularArithmetic/ModularMultiplicativeInverse.cs b/Algorithms/ModularArithmetic/ModularMultiplicativeInverse.cs index 0e42cd68..9a3c5ce0 100644 --- a/Algorithms/ModularArithmetic/ModularMultiplicativeInverse.cs +++ b/Algorithms/ModularArithmetic/ModularMultiplicativeInverse.cs @@ -20,13 +20,13 @@ public static long Compute(long a, long n) var eeaResult = ExtendedEuclideanAlgorithm.Compute(a, n); // Check if there is an inverse: - if (eeaResult.gcd != 1) + if (eeaResult.Gcd != 1) { throw new ArithmeticException($"{a} is not invertible in Z/{n}Z."); } // Make sure, inverseOfA (i.e. the bezout coefficient of a) is in the interval [0, n). - var inverseOfA = eeaResult.bezoutA; + var inverseOfA = eeaResult.BezoutA; if (inverseOfA < 0) { inverseOfA += n; @@ -47,13 +47,13 @@ public static BigInteger Compute(BigInteger a, BigInteger n) var eeaResult = ExtendedEuclideanAlgorithm.Compute(a, n); // Check if there is an inverse: - if (eeaResult.gcd != 1) + if (eeaResult.Gcd != 1) { throw new ArithmeticException($"{a} is not invertible in Z/{n}Z."); } // Make sure, inverseOfA (i.e. the bezout coefficient of a) is in the interval [0, n). - var inverseOfA = eeaResult.bezoutA; + var inverseOfA = eeaResult.BezoutA; if (inverseOfA < 0) { inverseOfA += n; diff --git a/Algorithms/Other/FloodFill.cs b/Algorithms/Other/FloodFill.cs index 22cc94e1..ac881d5f 100644 --- a/Algorithms/Other/FloodFill.cs +++ b/Algorithms/Other/FloodFill.cs @@ -14,7 +14,7 @@ namespace Algorithms.Other; /// public static class FloodFill { - private static readonly List<(int xOffset, int yOffset)> Neighbors = new() { (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1) }; + private static readonly List<(int XOffset, int YOffset)> Neighbors = new() { (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1) }; /// /// Implements the flood fill algorithm through a breadth-first approach using a queue. @@ -23,14 +23,14 @@ public static class FloodFill /// The start location on the bitmap. /// The old color to be replaced. /// The new color to replace the old one. - public static void BreadthFirstSearch(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor) + public static void BreadthFirstSearch(SKBitmap bitmap, (int X, int Y) location, SKColor targetColor, SKColor replacementColor) { - if (location.x < 0 || location.x >= bitmap.Width || location.y < 0 || location.y >= bitmap.Height) + if (location.X < 0 || location.X >= bitmap.Width || location.Y < 0 || location.Y >= bitmap.Height) { throw new ArgumentOutOfRangeException(nameof(location), $"{nameof(location)} should point to a pixel within the bitmap"); } - var queue = new List<(int x, int y)>(); + var queue = new List<(int X, int Y)>(); queue.Add(location); while (queue.Count > 0) @@ -46,9 +46,9 @@ public static void BreadthFirstSearch(SKBitmap bitmap, (int x, int y) location, /// The start location on the bitmap. /// The old color to be replaced. /// The new color to replace the old one. - public static void DepthFirstSearch(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor) + public static void DepthFirstSearch(SKBitmap bitmap, (int X, int Y) location, SKColor targetColor, SKColor replacementColor) { - if (location.x < 0 || location.x >= bitmap.Width || location.y < 0 || location.y >= bitmap.Height) + if (location.X < 0 || location.X >= bitmap.Width || location.Y < 0 || location.Y >= bitmap.Height) { throw new ArgumentOutOfRangeException(nameof(location), $"{nameof(location)} should point to a pixel within the bitmap"); } @@ -56,19 +56,19 @@ public static void DepthFirstSearch(SKBitmap bitmap, (int x, int y) location, SK DepthFirstFill(bitmap, location, targetColor, replacementColor); } - private static void BreadthFirstFill(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor, List<(int x, int y)> queue) + private static void BreadthFirstFill(SKBitmap bitmap, (int X, int Y) location, SKColor targetColor, SKColor replacementColor, List<(int X, int Y)> queue) { - (int x, int y) currentLocation = queue[0]; + (int X, int Y) currentLocation = queue[0]; queue.RemoveAt(0); - if (bitmap.GetPixel(currentLocation.x, currentLocation.y) == targetColor) + if (bitmap.GetPixel(currentLocation.X, currentLocation.Y) == targetColor) { - bitmap.SetPixel(currentLocation.x, currentLocation.y, replacementColor); + bitmap.SetPixel(currentLocation.X, currentLocation.Y, replacementColor); for (int i = 0; i < Neighbors.Count; i++) { - int x = currentLocation.x + Neighbors[i].xOffset; - int y = currentLocation.y + Neighbors[i].yOffset; + int x = currentLocation.X + Neighbors[i].XOffset; + int y = currentLocation.Y + Neighbors[i].YOffset; if (x >= 0 && x < bitmap.Width && y >= 0 && y < bitmap.Height) { queue.Add((x, y)); @@ -77,16 +77,16 @@ private static void BreadthFirstFill(SKBitmap bitmap, (int x, int y) location, S } } - private static void DepthFirstFill(SKBitmap bitmap, (int x, int y) location, SKColor targetColor, SKColor replacementColor) + private static void DepthFirstFill(SKBitmap bitmap, (int X, int Y) location, SKColor targetColor, SKColor replacementColor) { - if (bitmap.GetPixel(location.x, location.y) == targetColor) + if (bitmap.GetPixel(location.X, location.Y) == targetColor) { - bitmap.SetPixel(location.x, location.y, replacementColor); + bitmap.SetPixel(location.X, location.Y, replacementColor); for (int i = 0; i < Neighbors.Count; i++) { - int x = location.x + Neighbors[i].xOffset; - int y = location.y + Neighbors[i].yOffset; + int x = location.X + Neighbors[i].XOffset; + int y = location.Y + Neighbors[i].YOffset; if (x >= 0 && x < bitmap.Width && y >= 0 && y < bitmap.Height) { DepthFirstFill(bitmap, (x, y), targetColor, replacementColor); diff --git a/Algorithms/Other/GaussOptimization.cs b/Algorithms/Other/GaussOptimization.cs index 3387a1e1..a8c50fdb 100644 --- a/Algorithms/Other/GaussOptimization.cs +++ b/Algorithms/Other/GaussOptimization.cs @@ -24,7 +24,7 @@ public class GaussOptimization /// The first function parameter. /// The second function parameter. /// A tuple of coordinates of function extremum. - public (double, double) Optimize( + public (double X1, double X2) Optimize( Func func, double n, double step, diff --git a/Algorithms/Other/RGBHSVConversion.cs b/Algorithms/Other/RGBHSVConversion.cs index ecad76d3..9e1f2f97 100644 --- a/Algorithms/Other/RGBHSVConversion.cs +++ b/Algorithms/Other/RGBHSVConversion.cs @@ -22,7 +22,7 @@ public static class RgbHsvConversion /// Saturation of the color. /// Brightness-value of the color. /// The tuple of RGB-components. - public static (byte red, byte green, byte blue) HsvToRgb( + public static (byte Red, byte Green, byte Blue) HsvToRgb( double hue, double saturation, double value) @@ -59,7 +59,7 @@ public static (byte red, byte green, byte blue) HsvToRgb( /// Green-component of the color. /// Blue-component of the color. /// The tuple of HSV-components. - public static (double hue, double saturation, double value) RgbToHsv( + public static (double Hue, double Saturation, double Value) RgbToHsv( byte red, byte green, byte blue) @@ -94,7 +94,7 @@ public static (double hue, double saturation, double value) RgbToHsv( return (hue, saturation, value); } - private static (byte red, byte green, byte blue) GetRgbBySection( + private static (byte Red, byte Green, byte Blue) GetRgbBySection( double hueSection, double chroma, double matchValue, diff --git a/Algorithms/Search/FastSearcher.cs b/Algorithms/Search/FastSearcher.cs index b11989df..42b2cf3a 100644 --- a/Algorithms/Search/FastSearcher.cs +++ b/Algorithms/Search/FastSearcher.cs @@ -44,7 +44,7 @@ public int FindIndex(Span array, int item) return from + FindIndex(array.Slice(from, to - from + 1), item); } - private (int left, int right) ComputeIndices(Span array, int item) + private (int Left, int Right) ComputeIndices(Span array, int item) { var indexBinary = array.Length / 2; @@ -62,7 +62,7 @@ public int FindIndex(Span array, int item) : (indexInterpolation, indexBinary); } - private (int from, int to) SelectSegment(Span array, int left, int right, int item) + private (int From, int To) SelectSegment(Span array, int left, int right, int item) { if (item < array[left]) { diff --git a/Algorithms/Sorters/Comparison/MergeSorter.cs b/Algorithms/Sorters/Comparison/MergeSorter.cs index b86af4b6..13a888ad 100644 --- a/Algorithms/Sorters/Comparison/MergeSorter.cs +++ b/Algorithms/Sorters/Comparison/MergeSorter.cs @@ -57,7 +57,7 @@ private static void Merge(T[] array, T[] left, T[] right, IComparer comparer) } } - private static (T[] left, T[] right) Split(T[] array) + private static (T[] Left, T[] Right) Split(T[] array) { var mid = array.Length / 2; return (array.Take(mid).ToArray(), array.Skip(mid).ToArray()); diff --git a/Algorithms/Sorters/Comparison/TimSorter.cs b/Algorithms/Sorters/Comparison/TimSorter.cs index 0115e560..d098d340 100755 --- a/Algorithms/Sorters/Comparison/TimSorter.cs +++ b/Algorithms/Sorters/Comparison/TimSorter.cs @@ -9,18 +9,18 @@ namespace Algorithms.Sorters.Comparison; /// It was originally implemented by Tim Peters in 2002 for use in the Python programming language. /// /// This class is based on a Java interpretation of Tim Peter's original work. -/// Java class is viewable here: +/// Java class is viewable here: /// http://cr.openjdk.java.net/~martin/webrevs/openjdk7/timsort/raw_files/new/src/share/classes/java/util/TimSort.java /// -/// Tim Peters's list sort for Python, is described in detail here: -/// http://svn.python.org/projects/python/trunk/Objects/listsort.txt +/// Tim Peters's list sort for Python, is described in detail here: +/// http://svn.python.org/projects/python/trunk/Objects/listsort.txt /// /// Tim's C code may be found here: http://svn.python.org/projects/python/trunk/Objects/listobject.c /// -/// The underlying techniques are described in this paper (and may have even earlier origins): -/// "Optimistic Sorting and Information Theoretic Complexity" -/// Peter McIlroy -/// SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), +/// The underlying techniques are described in this paper (and may have even earlier origins): +/// "Optimistic Sorting and Information Theoretic Complexity" +/// Peter McIlroy +/// SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), /// pp 467-474, Austin, Texas, 25-27 January 1993. /// /// Type of array element. @@ -34,31 +34,31 @@ public class TimSorter : IComparisonSorter private readonly int[] runBase; private readonly int[] runLengths; - + private int minGallop; private int stackSize; private IComparer comparer = default!; - /// - /// Private class for handling gallop merges, allows for tracking array indexes and wins. - /// - /// Type of array element. - private class TimChunk - { - public Tc[] Array { get; set; } = default!; - - public int Index { get; set; } - - public int Remaining { get; set; } - - public int Wins { get; set; } + /// + /// Private class for handling gallop merges, allows for tracking array indexes and wins. + /// + /// Type of array element. + private class TimChunk + { + public Tc[] Array { get; set; } = default!; + + public int Index { get; set; } + + public int Remaining { get; set; } + + public int Wins { get; set; } } public TimSorter(TimSorterSettings settings, IComparer comparer) { initMinGallop = minGallop; - runBase = new int[85]; + runBase = new int[85]; runLengths = new int[85]; stackSize = 0; @@ -149,21 +149,21 @@ private static int MinRunLength(int total, int minRun) return total + r; } - /// - /// Reverse the specified range of the specified array. - /// - /// the array in which a range is to be reversed. - /// the index of the first element in the range to be reversed. - /// the index after the last element in the range to be reversed. - private static void ReverseRange(T[] array, int start, int end) - { - end--; - while (start < end) - { - var t = array[start]; - array[start++] = array[end]; - array[end--] = t; - } + /// + /// Reverse the specified range of the specified array. + /// + /// the array in which a range is to be reversed. + /// the index of the first element in the range to be reversed. + /// the index after the last element in the range to be reversed. + private static void ReverseRange(T[] array, int start, int end) + { + end--; + while (start < end) + { + var t = array[start]; + array[start++] = array[end]; + array[end--] = t; + } } /// @@ -175,18 +175,18 @@ private static void ReverseRange(T[] array, int start, int end) /// If a merge is required. private static bool NeedsMerge(TimChunk left, TimChunk right, ref int dest) { - right.Array[dest++] = right.Array[right.Index++]; + right.Array[dest++] = right.Array[right.Index++]; if (--right.Remaining == 0) - { - Array.Copy(left.Array, left.Index, right.Array, dest, left.Remaining); - return false; + { + Array.Copy(left.Array, left.Index, right.Array, dest, left.Remaining); + return false; } - + if (left.Remaining == 1) - { - Array.Copy(right.Array, right.Index, right.Array, dest, right.Remaining); - right.Array[dest + right.Remaining] = left.Array[left.Index]; - return false; + { + Array.Copy(right.Array, right.Index, right.Array, dest, right.Remaining); + right.Array[dest + right.Remaining] = left.Array[left.Index]; + return false; } return true; @@ -201,71 +201,71 @@ private static bool NeedsMerge(TimChunk left, TimChunk right, ref int dest private static void FinalizeMerge(TimChunk left, TimChunk right, int dest) { if (left.Remaining == 1) - { - Array.Copy(right.Array, right.Index, right.Array, dest, right.Remaining); - right.Array[dest + right.Remaining] = left.Array[left.Index]; + { + Array.Copy(right.Array, right.Index, right.Array, dest, right.Remaining); + right.Array[dest + right.Remaining] = left.Array[left.Index]; } else if (left.Remaining == 0) - { - throw new ArgumentException("Comparison method violates its general contract!"); + { + throw new ArgumentException("Comparison method violates its general contract!"); } else - { - Array.Copy(left.Array, left.Index, right.Array, dest, left.Remaining); + { + Array.Copy(left.Array, left.Index, right.Array, dest, left.Remaining); } } - /// - /// Returns the length of the run beginning at the specified position in - /// the specified array and reverses the run if it is descending (ensuring - /// that the run will always be ascending when the method returns). + /// + /// Returns the length of the run beginning at the specified position in + /// the specified array and reverses the run if it is descending (ensuring + /// that the run will always be ascending when the method returns). /// - /// A run is the longest ascending sequence with: + /// A run is the longest ascending sequence with: /// - /// + /// /// - /// or the longest descending sequence with: + /// or the longest descending sequence with: /// - /// a[lo + 1] > a[lo + 2] > ...]]> + /// a[lo + 1] > a[lo + 2] > ...]]> /// - /// For its intended use in a stable mergesort, the strictness of the - /// definition of "descending" is needed so that the call can safely - /// reverse a descending sequence without violating stability. - /// - /// the array in which a run is to be counted and possibly reversed. - /// index of the first element in the run. - /// the length of the run beginning at the specified position in the specified array. + /// For its intended use in a stable mergesort, the strictness of the + /// definition of "descending" is needed so that the call can safely + /// reverse a descending sequence without violating stability. + /// + /// the array in which a run is to be counted and possibly reversed. + /// index of the first element in the run. + /// the length of the run beginning at the specified position in the specified array. private int CountRunAndMakeAscending(T[] array, int start) - { - var runHi = start + 1; + { + var runHi = start + 1; if (runHi == array.Length) - { + { return 1; - } - - // Find end of run, and reverse range if descending + } + + // Find end of run, and reverse range if descending if (comparer.Compare(array[runHi++], array[start]) < 0) - { // Descending + { // Descending while (runHi < array.Length && comparer.Compare(array[runHi], array[runHi - 1]) < 0) - { + { runHi++; } - + ReverseRange(array, start, runHi); } else - { // Ascending + { // Ascending while (runHi < array.Length && comparer.Compare(array[runHi], array[runHi - 1]) >= 0) - { + { runHi++; - } - } - - return runHi - start; + } + } + + return runHi - start; } /// - /// Sorts the specified portion of the specified array using a binary + /// Sorts the specified portion of the specified array using a binary /// insertion sort. It requires O(n log n) compares, but O(n^2) data movement. /// /// Array to sort. @@ -334,25 +334,25 @@ private void MergeCollapse(T[] array) } } - private void MergeForceCollapse(T[] array) - { - while (stackSize > 1) - { - var n = stackSize - 2; + private void MergeForceCollapse(T[] array) + { + while (stackSize > 1) + { + var n = stackSize - 2; if (n > 0 && runLengths[n - 1] < runLengths[n + 1]) { n--; } - - MergeAt(array, n); - } + + MergeAt(array, n); + } } private void MergeAt(T[] array, int index) { - var baseA = runBase[index]; - var lenA = runLengths[index]; - var baseB = runBase[index + 1]; + var baseA = runBase[index]; + var lenA = runLengths[index]; + var baseB = runBase[index + 1]; var lenB = runLengths[index + 1]; runLengths[index] = lenA + lenB; @@ -386,45 +386,45 @@ private void MergeAt(T[] array, int index) } private void Merge(T[] array, int baseA, int lenA, int baseB, int lenB) - { - var endA = baseA + lenA; + { + var endA = baseA + lenA; var dest = baseA; - TimChunk left = new() - { - Array = array[baseA..endA], - Remaining = lenA, - }; - - TimChunk right = new() - { - Array = array, - Index = baseB, - Remaining = lenB, - }; - - // Move first element of the right chunk and deal with degenerate cases. + TimChunk left = new() + { + Array = array[baseA..endA], + Remaining = lenA, + }; + + TimChunk right = new() + { + Array = array, + Index = baseB, + Remaining = lenB, + }; + + // Move first element of the right chunk and deal with degenerate cases. if (!TimSorter.NeedsMerge(left, right, ref dest)) { // One of the chunks had 0-1 items in it, so no need to merge anything. return; } - + var gallop = minGallop; - + while (RunMerge(left, right, ref dest, ref gallop)) { // Penalize for leaving gallop mode gallop = gallop > 0 ? gallop + 2 - : 2; + : 2; } minGallop = gallop >= 1 ? gallop - : 1; - - FinalizeMerge(left, right, dest); + : 1; + + FinalizeMerge(left, right, dest); } private bool RunMerge(TimChunk left, TimChunk right, ref int dest, ref int gallop) @@ -531,16 +531,3 @@ private bool GallopMerge(TimChunk left, TimChunk right, ref int dest) return false; } } - -public class TimSorterSettings -{ - public int MinMerge { get; } - - public int MinGallop { get; } - - public TimSorterSettings(int minMerge = 32, int minGallop = 7) - { - MinMerge = minMerge; - MinGallop = minGallop; - } -} diff --git a/Algorithms/Sorters/Comparison/TimSorterSettings.cs b/Algorithms/Sorters/Comparison/TimSorterSettings.cs new file mode 100644 index 00000000..0f804fbd --- /dev/null +++ b/Algorithms/Sorters/Comparison/TimSorterSettings.cs @@ -0,0 +1,14 @@ +namespace Algorithms.Sorters.Comparison; + +public class TimSorterSettings +{ + public int MinMerge { get; } + + public int MinGallop { get; } + + public TimSorterSettings(int minMerge = 32, int minGallop = 7) + { + MinMerge = minMerge; + MinGallop = minGallop; + } +} diff --git a/Algorithms/Sorters/Utils/GallopingStrategy.cs b/Algorithms/Sorters/Utils/GallopingStrategy.cs index 2226064b..4c4ddc02 100644 --- a/Algorithms/Sorters/Utils/GallopingStrategy.cs +++ b/Algorithms/Sorters/Utils/GallopingStrategy.cs @@ -40,7 +40,7 @@ public static int BoundLeftShift(int shiftable) => (shiftable << 1) < 0 ? (shiftable << 1) + 1 : int.MaxValue; - private static (int offset, int lastOfs) LeftRun(T[] array, T key, int baseIndex, int hint, IComparer comparer) + private static (int Offset, int LastOfs) LeftRun(T[] array, T key, int baseIndex, int hint, IComparer comparer) { var maxOfs = hint + 1; var (offset, tmp) = (1, 0); @@ -62,7 +62,7 @@ private static (int offset, int lastOfs) LeftRun(T[] array, T key, int baseIndex return (offset, lastOfs); } - private static (int offset, int lastOfs) RightRun(T[] array, T key, int baseIndex, int len, int hint, IComparer comparer) + private static (int Offset, int LastOfs) RightRun(T[] array, T key, int baseIndex, int len, int hint, IComparer comparer) { var (offset, lastOfs) = (1, 0); var maxOfs = len - hint; diff --git a/Algorithms/Strings/Similarity/CosineSimilarity.cs b/Algorithms/Strings/Similarity/CosineSimilarity.cs index d05ee8c9..2975f793 100644 --- a/Algorithms/Strings/Similarity/CosineSimilarity.cs +++ b/Algorithms/Strings/Similarity/CosineSimilarity.cs @@ -21,8 +21,8 @@ public static double Calculate(string left, string right) // Step 1: Get the vectors for the two strings // Each vector represents the frequency of each character in the string. var vectors = GetVectors(left.ToLowerInvariant(), right.ToLowerInvariant()); - var leftVector = vectors.leftVector; - var rightVector = vectors.rightVector; + var leftVector = vectors.LeftVector; + var rightVector = vectors.RightVector; // Step 2: Calculate the intersection of the two vectors // The intersection is the set of characters that appear in both strings. @@ -64,7 +64,7 @@ public static double Calculate(string left, string right) /// The first string. /// The second string. /// A tuple containing the vectors for the two strings. - private static (Dictionary leftVector, Dictionary rightVector) GetVectors(string left, string right) + private static (Dictionary LeftVector, Dictionary RightVector) GetVectors(string left, string right) { var leftVector = new Dictionary(); var rightVector = new Dictionary(); diff --git a/Algorithms/Strings/Similarity/DamerauLevenshteinDistance.cs b/Algorithms/Strings/Similarity/DamerauLevenshteinDistance.cs index a00bdae6..4d699658 100644 --- a/Algorithms/Strings/Similarity/DamerauLevenshteinDistance.cs +++ b/Algorithms/Strings/Similarity/DamerauLevenshteinDistance.cs @@ -36,7 +36,7 @@ public static int Calculate(string left, string right) // Calculate the minimum distance by considering three possible operations: // deletion, insertion, and substitution. distances[i, j] = Math.Min( - Math.Min( // deletion + Math.Min(// deletion distances[i - 1, j] + 1, // delete the character from the left string distances[i, j - 1] + 1), // insert the character into the right string distances[i - 1, j - 1] + cost); // substitute the character in the left string with the character in the right string diff --git a/DataStructures/AATree/AATree.cs b/DataStructures/AATree/AATree.cs index f4322cbf..c2f2eeae 100644 --- a/DataStructures/AATree/AATree.cs +++ b/DataStructures/AATree/AATree.cs @@ -216,7 +216,7 @@ private AaTreeNode Add(TKey key, AaTreeNode? node) throw new ArgumentException($"Key \"{key}\" already in tree!", nameof(key)); } - return Split(Skew(node)) !; + return Split(Skew(node))!; } /// diff --git a/DataStructures/DataStructures.csproj b/DataStructures/DataStructures.csproj index 06f645a3..445adbf9 100644 --- a/DataStructures/DataStructures.csproj +++ b/DataStructures/DataStructures.csproj @@ -16,7 +16,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/DataStructures/Graph/DirectedWeightedGraph.cs b/DataStructures/Graph/DirectedWeightedGraph.cs index 15e0a336..666b9e48 100644 --- a/DataStructures/Graph/DirectedWeightedGraph.cs +++ b/DataStructures/Graph/DirectedWeightedGraph.cs @@ -94,7 +94,7 @@ public void RemoveVertex(Vertex vertex) for (int i = indexToRemove; i < Count - 1; i++) { Vertices[i] = Vertices[i + 1]; - Vertices[i] !.Index = i; + Vertices[i]!.Index = i; } Vertices[Count - 1] = null; diff --git a/DataStructures/Hashing/HashTable.cs b/DataStructures/Hashing/HashTable.cs index 05dde26a..8f6aac78 100644 --- a/DataStructures/Hashing/HashTable.cs +++ b/DataStructures/Hashing/HashTable.cs @@ -150,7 +150,7 @@ public void Add(TKey? key, TValue? value) var index = GetIndex(key); if ( entries[index] != null && - EqualityComparer.Default.Equals(entries[index] !.Key!, key)) + EqualityComparer.Default.Equals(entries[index]!.Key!, key)) { throw new ArgumentException("Key already exists"); } diff --git a/DataStructures/RedBlackTree/RedBlackTreeNode.cs b/DataStructures/RedBlackTree/RedBlackTreeNode.cs index 5c888b38..91f42df0 100644 --- a/DataStructures/RedBlackTree/RedBlackTreeNode.cs +++ b/DataStructures/RedBlackTree/RedBlackTreeNode.cs @@ -6,12 +6,12 @@ namespace DataStructures.RedBlackTree; public enum NodeColor : byte { /// - /// Represents red node + /// Represents red node. /// Red, /// - /// Represents black node + /// Represents black node. /// Black, } diff --git a/DataStructures/ScapegoatTree/ScapegoatTree.cs b/DataStructures/ScapegoatTree/ScapegoatTree.cs index a5262152..8f0a1b55 100644 --- a/DataStructures/ScapegoatTree/ScapegoatTree.cs +++ b/DataStructures/ScapegoatTree/ScapegoatTree.cs @@ -247,7 +247,7 @@ public void Tune(double value) /// Scapegoat node with its parent node. Parent can be null if scapegoat node is root node. /// Thrown if path stack is empty. /// Thrown if scapegoat wasn't found. - public (Node? parent, Node scapegoat) FindScapegoatInPath(Stack> path) + public (Node? Parent, Node Scapegoat) FindScapegoatInPath(Stack> path) { if (path.Count == 0) { diff --git a/Utilities/Extensions/DictionaryExtensions.cs b/Utilities/Extensions/DictionaryExtensions.cs index 03638290..3d040554 100644 --- a/Utilities/Extensions/DictionaryExtensions.cs +++ b/Utilities/Extensions/DictionaryExtensions.cs @@ -17,7 +17,7 @@ public static class DictionaryExtensions /// public static void AddMany( this Dictionary keys, - IEnumerable<(TKey, TValue)> enumerable) where TKey : notnull + IEnumerable<(TKey Key, TValue Value)> enumerable) where TKey : notnull { foreach (var (key, value) in enumerable) { diff --git a/Utilities/Utilities.csproj b/Utilities/Utilities.csproj index 8c289a07..01944c82 100644 --- a/Utilities/Utilities.csproj +++ b/Utilities/Utilities.csproj @@ -16,7 +16,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 7f6eed475e56867c76a4287d92c546bd659bb17d Mon Sep 17 00:00:00 2001 From: Kalafatis Kwstas Date: Mon, 21 Oct 2024 22:05:31 +0300 Subject: [PATCH 2/2] Add Ascon Hash Algorithm (#479) --- .../Crypto/Digests/AsconDigestTests.cs | 266 +++++++++ .../Crypto/Exceptions/CryptoExceptionTests.cs | 69 +++ .../Exceptions/DataLengthExceptionTests.cs | 67 +++ .../Exceptions/OutputLengthExceptionTests.cs | 67 +++ .../Crypto/Utils/ByteEncodingUtils.cs | 81 +++ .../Crypto/Utils/LongUtilsTests.cs | 98 ++++ .../Crypto/Utils/ValidationUtilsTests.cs | 124 ++++ Algorithms/Crypto/Digests/AsconDigest.cs | 538 ++++++++++++++++++ Algorithms/Crypto/Digests/IDigest.cs | 70 +++ .../Crypto/Exceptions/CryptoException.cs | 36 ++ .../Crypto/Exceptions/DataLengthException.cs | 36 ++ .../Exceptions/OutputLengthException.cs | 57 ++ Algorithms/Crypto/Utils/ByteEncodingUtils.cs | 63 ++ Algorithms/Crypto/Utils/LongUtils.cs | 78 +++ Algorithms/Crypto/Utils/ValidationUtils.cs | 95 ++++ README.md | 3 +- 16 files changed, 1747 insertions(+), 1 deletion(-) create mode 100644 Algorithms.Tests/Crypto/Digests/AsconDigestTests.cs create mode 100644 Algorithms.Tests/Crypto/Exceptions/CryptoExceptionTests.cs create mode 100644 Algorithms.Tests/Crypto/Exceptions/DataLengthExceptionTests.cs create mode 100644 Algorithms.Tests/Crypto/Exceptions/OutputLengthExceptionTests.cs create mode 100644 Algorithms.Tests/Crypto/Utils/ByteEncodingUtils.cs create mode 100644 Algorithms.Tests/Crypto/Utils/LongUtilsTests.cs create mode 100644 Algorithms.Tests/Crypto/Utils/ValidationUtilsTests.cs create mode 100644 Algorithms/Crypto/Digests/AsconDigest.cs create mode 100644 Algorithms/Crypto/Digests/IDigest.cs create mode 100644 Algorithms/Crypto/Exceptions/CryptoException.cs create mode 100644 Algorithms/Crypto/Exceptions/DataLengthException.cs create mode 100644 Algorithms/Crypto/Exceptions/OutputLengthException.cs create mode 100644 Algorithms/Crypto/Utils/ByteEncodingUtils.cs create mode 100644 Algorithms/Crypto/Utils/LongUtils.cs create mode 100644 Algorithms/Crypto/Utils/ValidationUtils.cs diff --git a/Algorithms.Tests/Crypto/Digests/AsconDigestTests.cs b/Algorithms.Tests/Crypto/Digests/AsconDigestTests.cs new file mode 100644 index 00000000..bd83f2dd --- /dev/null +++ b/Algorithms.Tests/Crypto/Digests/AsconDigestTests.cs @@ -0,0 +1,266 @@ +using System; +using System.Text; +using Algorithms.Crypto.Digests; +using Algorithms.Crypto.Exceptions; +using FluentAssertions; +using NUnit.Framework; + +namespace Algorithms.Tests.Crypto.Digests; + +[NonParallelizable] +public class AsconDigestTests +{ + private readonly AsconDigest asconHash = new AsconDigest(AsconDigest.AsconParameters.AsconHash); + private readonly AsconDigest asconHashA = new AsconDigest(AsconDigest.AsconParameters.AsconHashA); + + [TestCase("a", "02a9d471afab12914197af7090f00d16c41b6e30be0a63bbfd00bc13064de548")] + [TestCase("abc", "d37fe9f1d10dbcfad8408a6804dbe91124a8912693322bb23ec1701e19e3fd51")] + [TestCase("Hello", "d80f38d94ad72bd18718879f753a44870e8446925ff64bd7441db5fe020b6c0c")] + [TestCase("message digest", "e8848979c5adfd21bfcf29e54be1dd085ee523d251e8e6876f2654d6368da0ca")] + [TestCase("abcdefghijklmnopqrstuvwxyz", "c62368674e1b2301f19f46c50bb7f87a988a3e41205d68ab9d7882d2a15e917b")] + [TestCase("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "4ff71928d740524735b5ab12bb1598463054f88089f3c5f9760b6bdcd23f897b")] + [TestCase("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "2dae8b553b93841120e88ee77b9ccb8b512a32318db6012025f3f1c482b1def8")] + public void AsconHash_ReturnsCorrectValue(string input, string expected) + { + var inputBytes = Encoding.ASCII.GetBytes(input); + var result = asconHash.Digest(inputBytes); + + result.Should().Be(expected); + } + + [TestCase("a", "062bb0346671da00da4f460308b4d2c4d9877c3e2827d6229ff5361332d36527")] + [TestCase("abc", "836a5ddba0142b011ce3425ea9789fd6a21628d619195a48c1540f847667a84e")] + [TestCase("Hello", "15f245df8af697dc540e86083822809ab7299575d8ad6c2e17ecc603a7ab79dd")] + [TestCase("message digest", "3f18a1f398a40a77e0e9477aa6cb50e9e1abecff651c1874f9717c02c8a165ba")] + [TestCase("abcdefghijklmnopqrstuvwxyz", "406b809260f361e12dcf0bf924bfe1ffd2f987fc18d90b94fc544ff80dc2946b")] + [TestCase("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "5c6c69ff3ee83361391b7236c8eb6718f52df43de5a61a4f4d2819d40430dc19")] + [TestCase("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "d8e38fc50d682550cd176decda61adb7fd1c793cdafa825f17f3a002d65847be")] + public void AsconHashA_ReturnsCorrectValue(string input, string expected) + { + var inputBytes = Encoding.ASCII.GetBytes(input); + var result = asconHashA.Digest(inputBytes); + + result.Should().Be(expected); + } + + [Test] + public void BlockUpdate_WithValidOffsetAndLength_ShouldProcessCorrectly() + { + // Arrange + var input = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99 }; + var offset = 2; + var length = 6; // Picking 6 bytes starting from offset 2 + + // Act + var act = () => asconHash.BlockUpdate(input, offset, length); + + // Assert + act.Should().NotThrow(); // Ensure no exceptions are thrown during processing + + // Finalize the hash and check the output size + var output = new byte[asconHash.GetDigestSize()]; + asconHash.DoFinal(output, 0); + output.Should().HaveCount(32); // Ascon hash size is 32 bytes + } + + [Test] + public void BlockUpdate_WithInvalidOffset_ShouldThrowDataLengthException() + { + // Arrange + var input = new byte[] { 0x00, 0x11, 0x22, 0x33 }; + var offset = 3; // Offset goes too close to the end + var length = 3; // Length would exceed buffer size + + // Act + var act = () => asconHash.BlockUpdate(input, offset, length); + + // Assert + act.Should().Throw() + .WithMessage("input buffer too short"); + } + + [Test] + public void BlockUpdate_WithInvalidLength_ShouldThrowDataLengthException() + { + // Arrange + var input = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + var offset = 1; // Valid offset + var length = 10; // Invalid length (exceeds buffer) + + // Act + var act = () => asconHash.BlockUpdate(input, offset, length); + + // Assert + act.Should().Throw() + .WithMessage("input buffer too short"); + } + + [Test] + public void BlockUpdate_WithPartialBlock_ShouldProcessCorrectly() + { + // Arrange + var input = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44 }; + var offset = 0; + var length = 5; // Less than 8 bytes, partial block + + // Act + asconHash.BlockUpdate(input, offset, length); + + // Assert + var output = new byte[asconHash.GetDigestSize()]; + asconHash.DoFinal(output, 0); + output.Should().HaveCount(32); // Ensure valid hash output + } + + [Test] + public void BlockUpdate_WithFullBlock_ShouldProcessCorrectly() + { + // Arrange + var input = new byte[] { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; + var offset = 0; + var length = 8; // Full block + + // Act + asconHash.BlockUpdate(input, offset, length); + + // Assert + var output = new byte[asconHash.GetDigestSize()]; + asconHash.DoFinal(output, 0); + output.Should().HaveCount(32); // Ensure valid hash output + } + + [Test] + public void BlockUpdate_MultipleCalls_ShouldProcessCorrectly() + { + // Arrange + var input1 = new byte[] { 0x00, 0x11, 0x22 }; + var input2 = new byte[] { 0x33, 0x44, 0x55, 0x66, 0x77 }; + + // Act + asconHash.BlockUpdate(input1, 0, input1.Length); + asconHash.BlockUpdate(input2, 0, input2.Length); + + // Assert + var output = new byte[asconHash.GetDigestSize()]; + asconHash.DoFinal(output, 0); + output.Should().HaveCount(32); // Ensure valid hash output + } + + [Test] + public void AsconHash_WhenGetNameIsCalled_ReturnsCorrectValue() + { + asconHash.AlgorithmName.Should().Be("Ascon-Hash"); + asconHashA.AlgorithmName.Should().Be("Ascon-HashA"); + } + + [Test] + public void AsconHash_WhenGetByteLengthIsCalled_ReturnsCorrectValue() + { + asconHash.GetByteLength().Should().Be(8); + } + + [Test] + public void Update_ShouldProcessByte_WhenBufferIsFull() + { + // Arrange + byte[] inputBytes = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; // 8 bytes to fill the buffer + + // Act + foreach (var input in inputBytes) + { + asconHashA.Update(input); + } + + // Assert + // Since the buffer is full after 8 updates, we expect the state to have been processed. + var output = new byte[asconHashA.GetDigestSize()]; + asconHashA.DoFinal(output, 0); + output.Should().HaveCount(32); // Ascon hash size is 32 bytes + } + + [Test] + public void Update_ShouldNotProcess_WhenBufferIsNotFull() + { + // Arrange + byte[] inputBytes = { 0x00, 0x11, 0x22, 0x33 }; // Only 4 bytes (buffer is not full) + + // Act + foreach (var input in inputBytes) + { + asconHashA.Update(input); + } + + // Assert + // Even though the buffer has received input, it should not process until it is full (8 bytes). + // We can check that DoFinal still completes, but the buffer has not been processed yet. + var output = new byte[asconHashA.GetDigestSize()]; + asconHashA.DoFinal(output, 0); + output.Should().HaveCount(32); // Ensure valid hash output + } + + [Test] + public void Update_ShouldProcessMultipleBlocks() + { + // Arrange + var inputBytes = new byte[16]; // Enough to fill two full blocks (16 bytes) + + // Act + foreach (var input in inputBytes) + { + asconHashA.Update(input); + } + + // Assert + // Ensure that the state is processed twice since 16 bytes were passed (2 blocks of 8 bytes). + var output = new byte[asconHashA.GetDigestSize()]; + asconHashA.DoFinal(output, 0); + output.Should().HaveCount(32); // Ensure valid hash output + } + + [Test] + public void Update_ShouldHandleSingleByteCorrectly() + { + // Arrange + byte input = 0xFF; // Single byte input + + // Act + asconHashA.Update(input); + + // Assert + // Even though one byte is provided, it should not process the state (waiting for 8 bytes). + var output = new byte[asconHashA.GetDigestSize()]; + asconHashA.DoFinal(output, 0); + output.Should().HaveCount(32); // Ensure valid hash output + } + + [Test] + public void Update_ShouldAccumulateStateWithMultipleUpdates() + { + // Arrange + byte[] inputBytes = { 0x00, 0x11, 0x22 }; // Partial input + + // Act + foreach (var input in inputBytes) + { + asconHashA.Update(input); + } + + // Add more data to fill the buffer. + byte[] additionalBytes = { 0x33, 0x44, 0x55, 0x66, 0x77 }; + foreach (var input in additionalBytes) + { + asconHashA.Update(input); + } + + // Assert + // Ensure that the state is correctly updated after multiple partial updates. + var output = new byte[asconHashA.GetDigestSize()]; + asconHashA.DoFinal(output, 0); + output.Should().HaveCount(32); // Ensure valid hash output + } + + private static string ToHexString(byte[] bytes) + { + return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant(); + } +} diff --git a/Algorithms.Tests/Crypto/Exceptions/CryptoExceptionTests.cs b/Algorithms.Tests/Crypto/Exceptions/CryptoExceptionTests.cs new file mode 100644 index 00000000..dc43c741 --- /dev/null +++ b/Algorithms.Tests/Crypto/Exceptions/CryptoExceptionTests.cs @@ -0,0 +1,69 @@ +using Algorithms.Crypto.Exceptions; +using NUnit.Framework; +using FluentAssertions; +using System; + + +namespace Algorithms.Tests.Crypto.Exceptions +{ + [TestFixture] + public class CryptoExceptionTests + { + [Test] + public void CryptoException_ShouldBeCreatedWithoutMessageOrInnerException() + { + // Act + var exception = new CryptoException(); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().NotBeNullOrEmpty(); + exception.InnerException.Should().BeNull(); + } + + [Test] + public void CryptoException_ShouldSetMessage() + { + // Arrange + var expectedMessage = "This is a custom cryptographic error."; + + // Act + var exception = new CryptoException(expectedMessage); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().Be(expectedMessage); + exception.InnerException.Should().BeNull(); + } + + [Test] + public void CryptoException_ShouldSetMessageAndInnerException() + { + // Arrange + var expectedMessage = "An error occurred during encryption."; + var innerException = new InvalidOperationException("Invalid operation"); + + // Act + var exception = new CryptoException(expectedMessage, innerException); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().Be(expectedMessage); + exception.InnerException.Should().Be(innerException); + } + + [Test] + public void CryptoException_MessageShouldNotBeNullWhenUsingDefaultConstructor() + { + // Act + var exception = new CryptoException(); + + // Assert + exception.Message.Should().NotBeNullOrEmpty(); // Even the default Exception message is not null or empty. + } + } +} + diff --git a/Algorithms.Tests/Crypto/Exceptions/DataLengthExceptionTests.cs b/Algorithms.Tests/Crypto/Exceptions/DataLengthExceptionTests.cs new file mode 100644 index 00000000..e792d4cb --- /dev/null +++ b/Algorithms.Tests/Crypto/Exceptions/DataLengthExceptionTests.cs @@ -0,0 +1,67 @@ +using NUnit.Framework; +using FluentAssertions; +using System; +using Algorithms.Crypto.Exceptions; + +namespace Algorithms.Tests.Crypto.Exceptions +{ + [TestFixture] + public class DataLengthExceptionTests + { + [Test] + public void DataLengthException_ShouldBeCreatedWithoutMessageOrInnerException() + { + // Act + var exception = new DataLengthException(); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().NotBeNullOrEmpty(); + exception.InnerException.Should().BeNull(); + } + + [Test] + public void DataLengthException_ShouldSetMessage() + { + // Arrange + var expectedMessage = "Data length is invalid."; + + // Act + var exception = new DataLengthException(expectedMessage); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().Be(expectedMessage); + exception.InnerException.Should().BeNull(); + } + + [Test] + public void DataLengthException_ShouldSetMessageAndInnerException() + { + // Arrange + var expectedMessage = "An error occurred due to incorrect data length."; + var innerException = new ArgumentException("Invalid argument"); + + // Act + var exception = new DataLengthException(expectedMessage, innerException); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().Be(expectedMessage); + exception.InnerException.Should().Be(innerException); + } + + [Test] + public void DataLengthException_MessageShouldNotBeNullWhenUsingDefaultConstructor() + { + // Act + var exception = new DataLengthException(); + + // Assert + exception.Message.Should().NotBeNullOrEmpty(); // Even the default Exception message is not null or empty. + } + } +} diff --git a/Algorithms.Tests/Crypto/Exceptions/OutputLengthExceptionTests.cs b/Algorithms.Tests/Crypto/Exceptions/OutputLengthExceptionTests.cs new file mode 100644 index 00000000..313b95ad --- /dev/null +++ b/Algorithms.Tests/Crypto/Exceptions/OutputLengthExceptionTests.cs @@ -0,0 +1,67 @@ +using NUnit.Framework; +using FluentAssertions; +using System; +using Algorithms.Crypto.Exceptions; + +namespace Algorithms.Tests.Crypto.Exceptions +{ + [TestFixture] + public class OutputLengthExceptionTests + { + [Test] + public void OutputLengthException_ShouldBeCreatedWithoutMessageOrInnerException() + { + // Act + var exception = new OutputLengthException(); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().NotBeNullOrEmpty(); + exception.InnerException.Should().BeNull(); + } + + [Test] + public void OutputLengthException_ShouldSetMessage() + { + // Arrange + var expectedMessage = "Output buffer is too short."; + + // Act + var exception = new OutputLengthException(expectedMessage); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().Be(expectedMessage); + exception.InnerException.Should().BeNull(); + } + + [Test] + public void OutputLengthException_ShouldSetMessageAndInnerException() + { + // Arrange + var expectedMessage = "Output length error."; + var innerException = new ArgumentException("Invalid argument"); + + // Act + var exception = new OutputLengthException(expectedMessage, innerException); + + // Assert + exception.Should().BeOfType() + .And.Subject.As() + .Message.Should().Be(expectedMessage); + exception.InnerException.Should().Be(innerException); + } + + [Test] + public void OutputLengthException_MessageShouldNotBeNullWhenUsingDefaultConstructor() + { + // Act + var exception = new OutputLengthException(); + + // Assert + exception.Message.Should().NotBeNullOrEmpty(); // Even the default Exception message is not null or empty. + } + } +} diff --git a/Algorithms.Tests/Crypto/Utils/ByteEncodingUtils.cs b/Algorithms.Tests/Crypto/Utils/ByteEncodingUtils.cs new file mode 100644 index 00000000..ef04168d --- /dev/null +++ b/Algorithms.Tests/Crypto/Utils/ByteEncodingUtils.cs @@ -0,0 +1,81 @@ +using NUnit.Framework; +using FluentAssertions; +using System; +using Algorithms.Crypto.Utils; + +namespace Algorithms.Tests.Crypto.Utils +{ + [TestFixture] + public class ByteEncodingUtilsTests + { + [Test] + public void BigEndianToUint64_ByteArray_ShouldConvertCorrectly() + { + // Arrange + byte[] input = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + var expected = 0x0123456789ABCDEFUL; + + // Act + var result = ByteEncodingUtils.BigEndianToUint64(input, 0); + + // Assert + result.Should().Be(expected); + } + + [Test] + public void BigEndianToUint64_ByteArray_WithOffset_ShouldConvertCorrectly() + { + // Arrange + byte[] input = { 0x00, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + var expected = 0x0123456789ABCDEFUL; + + // Act + var result = ByteEncodingUtils.BigEndianToUint64(input, 2); + + // Assert + result.Should().Be(expected); + } + + [Test] + public void BigEndianToUint64_Span_ShouldConvertCorrectly() + { + // Arrange + Span input = stackalloc byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + var expected = 0x0123456789ABCDEFUL; + + // Act + var result = ByteEncodingUtils.BigEndianToUint64(input); + + // Assert + result.Should().Be(expected); + } + + [Test] + public void UInt64ToBigEndian_ShouldWriteCorrectly() + { + // Arrange + var value = 0x0123456789ABCDEFUL; + Span output = stackalloc byte[8]; + byte[] expected = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + + // Act + ByteEncodingUtils.UInt64ToBigEndian(value, output); + + // Assert + output.ToArray().Should().Equal(expected); + } + + [Test] + public void BigEndianToUint64_InvalidOffset_ShouldThrowException() + { + // Arrange + byte[] input = { 0x01, 0x23 }; + + // Act + Action act = () => ByteEncodingUtils.BigEndianToUint64(input, 1); + + // Assert + act.Should().Throw(); + } + } +} diff --git a/Algorithms.Tests/Crypto/Utils/LongUtilsTests.cs b/Algorithms.Tests/Crypto/Utils/LongUtilsTests.cs new file mode 100644 index 00000000..cc2029bc --- /dev/null +++ b/Algorithms.Tests/Crypto/Utils/LongUtilsTests.cs @@ -0,0 +1,98 @@ +using NUnit.Framework; +using FluentAssertions; +using Algorithms.Crypto.Utils; + +namespace Algorithms.Tests.Crypto.Utils +{ + [TestFixture] + public class LongUtilsTests + { + [Test] + public void RotateLeft_Long_ShouldRotateCorrectly() + { + // Arrange + var input = 0x0123456789ABCDEF; + var distance = 8; + var expected = 0x23456789ABCDEF01L; // The expected result is a signed long value. + + // Act + var result = LongUtils.RotateLeft(input, distance); + + // Assert + result.Should().Be(expected); + } + + [Test] + public void RotateLeft_Ulong_ShouldRotateCorrectly() + { + // Arrange + var input = 0x0123456789ABCDEFUL; + var distance = 8; + var expected = 0x23456789ABCDEF01UL; // The expected result is an unsigned ulong value. + + // Act + var result = LongUtils.RotateLeft(input, distance); + + // Assert + result.Should().Be(expected); + } + + [Test] + public void RotateRight_Long_ShouldRotateCorrectly() + { + // Arrange + var input = 0x0123456789ABCDEF; + var distance = 8; + var expected = unchecked((long)0xEF0123456789ABCD); // Using unchecked to correctly represent signed long. + + // Act + var result = LongUtils.RotateRight(input, distance); + + // Assert + result.Should().Be(expected); + } + + [Test] + public void RotateRight_Ulong_ShouldRotateCorrectly() + { + // Arrange + var input = 0x0123456789ABCDEFUL; + var distance = 8; + var expected = 0xEF0123456789ABCDUL; // The expected result is an unsigned ulong value. + + // Act + var result = LongUtils.RotateRight(input, distance); + + // Assert + result.Should().Be(expected); + } + + [Test] + public void RotateLeft_Long_ShouldHandleZeroRotation() + { + // Arrange + var input = 0x0123456789ABCDEF; + var distance = 0; + + // Act + var result = LongUtils.RotateLeft(input, distance); + + // Assert + result.Should().Be(input); // No rotation, result should be the same as input. + } + + [Test] + public void RotateRight_Ulong_ShouldHandleFullRotation() + { + // Arrange + var input = 0x0123456789ABCDEFUL; + var distance = 64; + + // Act + var result = LongUtils.RotateRight(input, distance); + + // Assert + result.Should().Be(input); // Full 64-bit rotation should result in the same value. + } + } +} diff --git a/Algorithms.Tests/Crypto/Utils/ValidationUtilsTests.cs b/Algorithms.Tests/Crypto/Utils/ValidationUtilsTests.cs new file mode 100644 index 00000000..f109fcda --- /dev/null +++ b/Algorithms.Tests/Crypto/Utils/ValidationUtilsTests.cs @@ -0,0 +1,124 @@ +using NUnit.Framework; +using FluentAssertions; +using System; +using Algorithms.Crypto.Utils; +using Algorithms.Crypto.Exceptions; + +namespace Algorithms.Tests.Crypto.Utils +{ + [TestFixture] + public class ValidationUtilsTests + { + [Test] + public void CheckDataLength_WithBufferOutOfBounds_ShouldThrowDataLengthException() + { + // Arrange + var buffer = new byte[5]; // A byte array of length 5 + var offset = 3; // Starting at index 3 + var length = 4; // Expecting to read 4 bytes (which will exceed the buffer size) + var errorMessage = "Buffer is too short"; + + // Act + var act = () => ValidationUtils.CheckDataLength(buffer, offset, length, errorMessage); + + // Assert + act.Should().Throw() + .WithMessage(errorMessage); + } + + [Test] + public void CheckOutputLength_WithCondition_ShouldThrowOutputLengthException() + { + // Arrange + var condition = true; + var errorMessage = "Output length is invalid"; + + // Act + var act = () => ValidationUtils.CheckOutputLength(condition, errorMessage); + + // Assert + act.Should().Throw() + .WithMessage(errorMessage); + } + + [Test] + public void CheckOutputLength_WithCondition_ShouldNotThrowOutputLengthException() + { + // Arrange + var condition = false; + var errorMessage = "Output length is invalid"; + + // Act + var act = () => ValidationUtils.CheckOutputLength(condition, errorMessage); + + // Assert + act.Should().NotThrow(); + } + + [Test] + public void CheckOutputLength_WithBufferOutOfBounds_ShouldThrowOutputLengthException() + { + // Arrange + var buffer = new byte[5]; + var offset = 3; + var length = 4; + var errorMessage = "Output buffer is too short"; + + // Act + var act = () => ValidationUtils.CheckOutputLength(buffer, offset, length, errorMessage); + + // Assert + act.Should().Throw() + .WithMessage(errorMessage); + } + + [Test] + public void CheckOutputLength_WithBProperBufferSize_ShouldThrowOutputLengthException() + { + // Arrange + var buffer = new byte[5]; + var offset = 0; + var length = 4; + var errorMessage = "Output buffer is too short"; + + // Act + var act = () => ValidationUtils.CheckOutputLength(buffer, offset, length, errorMessage); + + // Assert + act.Should().NotThrow(); + } + + [Test] + public void CheckOutputLength_SpanExceedsLimit_ShouldThrowOutputLengthException() + { + // Arrange + Span output = new byte[10]; + var outputLength = output.Length; + var maxLength = 5; + var errorMessage = "Output exceeds maximum length"; + + // Act + var act = () => ValidationUtils.CheckOutputLength(outputLength > maxLength, errorMessage); // Capture the length + + // Assert + act.Should().Throw() + .WithMessage(errorMessage); + } + + [Test] + public void CheckOutputLength_SpanDoesNotExceedLimit_ShouldThrowOutputLengthException() + { + // Arrange + Span output = new byte[10]; + var outputLength = output.Length; + var maxLength = 15; + var errorMessage = "Output exceeds maximum length"; + + // Act + var act = () => ValidationUtils.CheckOutputLength(outputLength > maxLength, errorMessage); // Capture the length + + // Assert + act.Should().NotThrow(); + } + } +} diff --git a/Algorithms/Crypto/Digests/AsconDigest.cs b/Algorithms/Crypto/Digests/AsconDigest.cs new file mode 100644 index 00000000..d6ec3c97 --- /dev/null +++ b/Algorithms/Crypto/Digests/AsconDigest.cs @@ -0,0 +1,538 @@ +using System; +using System.Runtime.CompilerServices; +using Algorithms.Crypto.Utils; + +namespace Algorithms.Crypto.Digests; + +/// +/// Implements the Ascon cryptographic hash algorithm, providing both the standard Ascon-Hash and the Ascon-HashA variants. +/// +/// +/// The class implements the Ascon hash function, a lightweight cryptographic algorithm designed for +/// resource-constrained environments such as IoT devices. It provides two variants: +/// +/// +/// +/// : The standard Ascon-Hash variant with 12 rounds of the permutation function for enhanced security. +/// +/// +/// +/// +/// : A performance-optimized variant with 8 rounds of the permutation function, offering a trade-off between security and performance. +/// +/// +/// +///
+/// The AsconDigest processes data in 8-byte blocks, accumulating input until a block is complete, at which point it applies +/// the permutation function to update the internal state. After all data has been processed, the hash value can be finalized +/// and retrieved. +///
+/// Ascon was designed to meet the requirements of lightweight cryptography, making it ideal for devices with limited computational power. +///
+public class AsconDigest : IDigest +{ + public enum AsconParameters + { + /// + /// Represents the Ascon Hash variant, the standard cryptographic hashing function of the Ascon family. + /// + /// + /// AsconHash is the primary hashing algorithm in the Ascon family. It is designed for efficiency and security + /// in resource-constrained environments, such as IoT devices, and provides high resistance to cryptanalytic attacks. + /// This variant uses 12 rounds of the permutation function for increased security. + /// + AsconHash, + + /// + /// Represents the Ascon HashA variant, an alternative variant of the Ascon hashing function with fewer permutation rounds. + /// + /// + /// AsconHashA is a variant of the Ascon hashing function that uses fewer rounds (8 rounds) of the permutation function, + /// trading off some security for improved performance in specific scenarios. It is still designed to be secure for many + /// applications, but it operates faster in environments where computational resources are limited. + /// + AsconHashA, + } + + /// + /// Specifies the Ascon variant being used (either Ascon-Hash or Ascon-HashA). This defines the cryptographic algorithm's behavior. + /// + private readonly AsconParameters asconParameters; + + /// + /// The number of permutation rounds applied in the Ascon cryptographic process. This is determined by the selected Ascon variant. + /// + private readonly int asconPbRounds; + + /// + /// Internal buffer that temporarily stores input data before it is processed in 8-byte blocks. The buffer is cleared after each block is processed. + /// + private readonly byte[] buffer = new byte[8]; + + /// + /// Internal state variable x0 used in the cryptographic permutation function. This is updated continuously as input data is processed. + /// + private ulong x0; + + /// + /// Internal state variable x1 used in the cryptographic permutation function. This, along with other state variables, is updated during each round. + /// + private ulong x1; + + /// + /// Internal state variable x2 used in the cryptographic permutation function. It helps track the evolving state of the digest. + /// + private ulong x2; + + /// + /// Internal state variable x3 used in the cryptographic permutation function, contributing to the mixing and non-linearity of the state. + /// + private ulong x3; + + /// + /// Internal state variable x4 used in the cryptographic permutation function. This, along with x0 to x3, ensures cryptographic security. + /// + private ulong x4; + + /// + /// Tracks the current position within the buffer array. When bufferPosition reaches 8, the buffer is processed and reset. + /// + private int bufferPosition; + + /// + /// Initializes a new instance of the class with the specified Ascon parameters. + /// + /// The Ascon variant to use, either or . + /// + /// This constructor sets up the digest by selecting the appropriate number of permutation rounds based on the Ascon variant. + /// + /// For , 12 permutation rounds are used. + /// For , 8 permutation rounds are used. + /// + /// If an unsupported parameter is provided, the constructor throws an to indicate that the parameter is invalid. + /// The internal state of the digest is then reset to prepare for processing input data. + /// + /// Thrown when an invalid parameter setting is provided for Ascon Hash. + public AsconDigest(AsconParameters parameters) + { + // Set the Ascon parameter (AsconHash or AsconHashA) for this instance. + asconParameters = parameters; + + // Determine the number of permutation rounds based on the Ascon variant. + asconPbRounds = parameters switch + { + AsconParameters.AsconHash => 12, // 12 rounds for Ascon-Hash variant. + AsconParameters.AsconHashA => 8, // 8 rounds for Ascon-HashA variant. + _ => throw new ArgumentException("Invalid parameter settings for Ascon Hash"), // Throw exception for invalid parameter. + }; + + // Reset the internal state to prepare for new input. + Reset(); + } + + /// + /// Gets the name of the cryptographic algorithm based on the selected Ascon parameter. + /// + /// + /// A string representing the name of the algorithm variant, either "Ascon-Hash" or "Ascon-HashA". + /// + /// + /// This property determines the algorithm name based on the selected Ascon variant when the instance was initialized. + /// It supports two variants: + /// + /// "Ascon-Hash" for the variant. + /// "Ascon-HashA" for the variant. + /// + /// If an unsupported or unknown parameter is used, the property throws an . + /// + /// Thrown if an unknown Ascon parameter is encountered. + public string AlgorithmName + { + get + { + return asconParameters switch + { + AsconParameters.AsconHash => "Ascon-Hash", // Return "Ascon-Hash" for AsconHash variant. + AsconParameters.AsconHashA => "Ascon-HashA", // Return "Ascon-HashA" for AsconHashA variant. + _ => throw new InvalidOperationException(), // Throw an exception for unknown Ascon parameters. + }; + } + } + + /// + /// Gets the size of the resulting hash produced by the digest, in bytes. + /// + /// The size of the hash, which is 32 bytes (256 bits) for this digest implementation. + /// + /// This method returns the fixed size of the hash output produced by the digest algorithm. In this implementation, + /// the digest produces a 256-bit hash, which corresponds to 32 bytes. This is typical for cryptographic hash functions + /// that aim to provide a high level of security by generating a large output size. + /// + public int GetDigestSize() => 32; + + /// + /// Gets the internal block size of the digest in bytes. + /// + /// The internal block size of the digest, which is 8 bytes (64 bits). + /// + /// This method returns the block size that the digest algorithm uses when processing input data. The input is processed + /// in chunks (blocks) of 8 bytes at a time. This block size determines how the input data is split and processed in multiple + /// steps before producing the final hash. + /// + public int GetByteLength() => 8; + + /// + /// Updates the cryptographic state by processing a single byte of input and adding it to the internal buffer. + /// + /// The byte to be added to the internal buffer and processed. + /// + /// This method collects input bytes in an internal buffer. Once the buffer is filled (reaching 8 bytes), the buffer is processed + /// by converting it into a 64-bit unsigned integer in big-endian format and XORing it with the internal state variable x0. + /// After processing the buffer, the permutation function is applied to mix the internal state, and the buffer position is reset to zero. + ///

+ /// If the buffer has not yet reached 8 bytes, the method simply adds the input byte to the buffer and waits for further input. + ///
+ public void Update(byte input) + { + // Add the input byte to the buffer. + buffer[bufferPosition] = input; + + // If the buffer is not full (less than 8 bytes), increment the buffer position and return early. + if (++bufferPosition != 8) + { + return; // Wait for more input to fill the buffer before processing. + } + + // Once the buffer is full (8 bytes), convert the buffer to a 64-bit integer (big-endian) and XOR it with the state. + x0 ^= ByteEncodingUtils.BigEndianToUint64(buffer, 0); + + // Apply the permutation function to mix the state. + P(asconPbRounds); + + // Reset the buffer position for the next block of input. + bufferPosition = 0; + } + + /// + /// Updates the cryptographic state by processing a segment of input data from a byte array, starting at a specified offset and length. + /// + /// The byte array containing the input data to be processed. + /// The offset in the input array where processing should begin. + /// The number of bytes from the input array to process. + /// + /// This method ensures that the input data is valid by checking the array length, starting from the provided offset, + /// and making sure it is long enough to accommodate the specified length. It then processes the data by converting + /// the relevant section of the byte array to a and delegating the actual block update to + /// the method for further processing. + /// + /// + /// Thrown if the input data is too short, starting from and for the length . + /// + public void BlockUpdate(byte[] input, int inOff, int inLen) + { + // Validate the input data to ensure there is enough data to process from the specified offset and length. + ValidationUtils.CheckDataLength(input, inOff, inLen, "input buffer too short"); + + // Convert the input byte array into a ReadOnlySpan and delegate the processing to the span-based method. + BlockUpdate(input.AsSpan(inOff, inLen)); + } + + /// + /// Processes the input data by updating the internal cryptographic state, handling both partial and full blocks. + /// + /// A read-only span of bytes representing the input data to be processed. + /// + /// This method processes the input data in chunks of 8 bytes. It manages the internal buffer to accumulate data + /// until there are enough bytes to process a full 8-byte block. When the buffer is full or enough input is provided, + /// it XORs the buffered data with the internal state variable x0 and applies the permutation function + /// to update the cryptographic state. + ///

+ /// If the input contains more than 8 bytes, the method continues to process full 8-byte blocks in a loop until + /// the input is exhausted. Any remaining bytes (less than 8) are stored in the internal buffer for future processing. + ///
+ public void BlockUpdate(ReadOnlySpan input) + { + // Calculate the number of available bytes left in the buffer before it reaches 8 bytes. + var available = 8 - bufferPosition; + + // If the input length is smaller than the remaining space in the buffer, copy the input into the buffer. + if (input.Length < available) + { + input.CopyTo(buffer.AsSpan(bufferPosition)); // Copy the small input into the buffer. + bufferPosition += input.Length; // Update the buffer position. + return; // Return early since we don't have enough data to process a full block. + } + + // If there is data in the buffer, but it isn't full, fill it and process the full 8-byte block. + if (bufferPosition > 0) + { + // Copy enough bytes from the input to complete the buffer. + input[..available].CopyTo(buffer.AsSpan(bufferPosition)); + + // XOR the full buffer with the internal state (x0) and apply the permutation. + x0 ^= ByteEncodingUtils.BigEndianToUint64(buffer); + P(asconPbRounds); // Apply the permutation rounds. + + // Update the input to exclude the bytes we've already processed from the buffer. + input = input[available..]; + } + + // Process full 8-byte blocks directly from the input. + while (input.Length >= 8) + { + // XOR the next 8-byte block from the input with the internal state and apply the permutation. + x0 ^= ByteEncodingUtils.BigEndianToUint64(input); + P(asconPbRounds); + + // Move to the next 8-byte chunk in the input. + input = input[8..]; + } + + // Copy any remaining bytes (less than 8) into the buffer to store for future processing. + input.CopyTo(buffer); + bufferPosition = input.Length; // Update the buffer position to reflect the remaining unprocessed data. + } + + /// + /// Finalizes the cryptographic hash computation, absorbing any remaining data, applying the final permutation, + /// and writing the resulting hash to the specified position in the provided output byte array. + /// + /// The byte array where the final 32-byte hash will be written. + /// The offset in the output array at which to start writing the hash. + /// The size of the hash (32 bytes). + /// + /// This method finalizes the hash computation by converting the output array to a and + /// calling the method. It provides flexibility in placing the result in an + /// existing byte array with a specified offset. + /// + /// Thrown if the output buffer is too small to hold the resulting hash. + public int DoFinal(byte[] output, int outOff) + { + // Call the Span-based DoFinal method with the output byte array and offset. + return DoFinal(output.AsSpan(outOff)); + } + + /// + /// Finalizes the cryptographic hash computation, absorbing any remaining data, applying the final permutation, and + /// writing the resulting hash to the provided output buffer. + /// + /// A span of bytes where the final 32-byte hash will be written. + /// The size of the hash (32 bytes). + /// + /// This method completes the hash computation by absorbing any remaining input data, applying the final permutation, + /// and extracting the state variables to produce the final hash. The method processes the state in 8-byte chunks, + /// writing the result into the output buffer in big-endian format. After the final permutation is applied, the internal + /// state is reset to prepare for a new hashing session. + /// + /// Thrown if the output buffer is too small to hold the resulting hash. + public int DoFinal(Span output) + { + // Validate that the output buffer is at least 32 bytes in length. + ValidationUtils.CheckOutputLength(output, 32, "output buffer too short"); + + // Absorb any remaining input and apply the final permutation. + AbsorbAndFinish(); + + // Convert the first part of the state (x0) to big-endian format and write it to the output. + ByteEncodingUtils.UInt64ToBigEndian(x0, output); + + // Loop to process the remaining parts of the internal state (x1, x2, etc.). + for (var i = 0; i < 3; ++i) + { + // Move to the next 8-byte segment in the output buffer. + output = output[8..]; + + // Apply the permutation rounds to mix the state. + P(asconPbRounds); + + // Convert the updated state variable (x0) to big-endian format and write it to the output. + ByteEncodingUtils.UInt64ToBigEndian(x0, output); + } + + // Reset the internal state for the next hash computation. + Reset(); + + // Return the size of the hash (32 bytes). + return 32; + } + + /// + /// Computes the cryptographic hash of the input byte array and returns the result as a lowercase hexadecimal string. + /// + /// The input byte array to be hashed. + /// A string containing the computed hash in lowercase hexadecimal format. + /// + /// This method takes a byte array as input, processes it to compute the Ascon hash, and returns the result as a hexadecimal string. + /// It internally converts the byte array to a and delegates the actual hashing to the + /// method. + /// + public string Digest(byte[] input) + { + return Digest(input.AsSpan()); + } + + /// + /// Computes the cryptographic hash of the input span of bytes and returns the result as a lowercase hexadecimal string. + /// + /// A span of bytes representing the input data to be hashed. + /// A string containing the computed hash in lowercase hexadecimal format. + /// + /// This method processes the input span using the Ascon cryptographic algorithm to compute the hash. It accumulates + /// the input, applies the necessary permutations and internal state updates, and finally produces a hash in the form + /// of a 32-byte array. The result is then converted into a lowercase hexadecimal string using . + /// + public string Digest(Span input) + { + // Update the internal state with the input data. + BlockUpdate(input); + + // Create an array to hold the final hash output (32 bytes). + var output = new byte[GetDigestSize()]; + + // Finalize the hash computation and store the result in the output array. + DoFinal(output, 0); + + // Convert the hash (byte array) to a lowercase hexadecimal string. + return BitConverter.ToString(output).Replace("-", string.Empty).ToLowerInvariant(); + } + + /// + /// Resets the internal state of the Ascon cryptographic hash algorithm to its initial state based on the selected variant. + /// + /// + /// This method clears the internal buffer and resets the buffer position to zero. Depending on the specified + /// Ascon variant ( or ), it also reinitializes + /// the internal state variables (x0, x1, x2, x3, x4) to their starting values. + ///

+ /// The reset is necessary to prepare the hash function for a new message. It ensures that previous messages do not + /// affect the new one and that the internal state is consistent with the algorithm’s specification for the selected variant. + ///
+ public void Reset() + { + // Clear the buffer to remove any leftover data from previous operations. + Array.Clear(buffer, 0, buffer.Length); + + // Reset the buffer position to zero to start processing fresh input. + bufferPosition = 0; + + // Initialize the internal state variables (x0, x1, x2, x3, x4) based on the selected Ascon variant. + switch (asconParameters) + { + // If using the AsconHashA variant, set the specific initial state values for x0 through x4. + case AsconParameters.AsconHashA: + x0 = 92044056785660070UL; + x1 = 8326807761760157607UL; + x2 = 3371194088139667532UL; + x3 = 15489749720654559101UL; + x4 = 11618234402860862855UL; + break; + + // If using the AsconHash variant, set the specific initial state values for x0 through x4. + case AsconParameters.AsconHash: + x0 = 17191252062196199485UL; + x1 = 10066134719181819906UL; + x2 = 13009371945472744034UL; + x3 = 4834782570098516968UL; + x4 = 3787428097924915520UL; + break; + + // If an unknown Ascon variant is encountered, throw an exception. + default: + throw new InvalidOperationException(); + } + } + + /// + /// Finalizes the absorption phase of the cryptographic hash by padding the buffer and applying the final permutation round. + /// + /// + /// This method is called when the input data has been fully absorbed into the internal state, and it needs to be finalized. + /// The buffer is padded with a specific value (0x80) to signify the end of the data, and the remaining portion of the buffer is + /// XORed with the internal state variable x0. After padding, the final permutation round is applied using 12 rounds of + /// the permutation function . This ensures the internal state is fully mixed and the cryptographic hash + /// is securely finalized. + /// + private void AbsorbAndFinish() + { + // Pad the buffer with 0x80 to indicate the end of the data. + buffer[bufferPosition] = 0x80; + + // XOR the buffer (after padding) with the internal state x0, but only the relevant portion of the buffer is considered. + // The (56 - (bufferPosition << 3)) shifts ensure that only the unprocessed part of the buffer is XORed into x0. + x0 ^= ByteEncodingUtils.BigEndianToUint64(buffer, 0) & (ulong.MaxValue << (56 - (bufferPosition << 3))); + + // Apply 12 rounds of the permutation function to fully mix and finalize the internal state. + P(12); + } + + /// + /// Executes the cryptographic permutation function by applying a sequence of rounds that transform the internal state variables. + /// + /// + /// The number of rounds to execute. If set to 12, additional rounds are performed with specific constants to enhance the security of the transformation. + /// + /// + /// In the Ascon cryptographic algorithm, the permutation function P transforms the internal state over multiple rounds. + /// This method applies a set of round constants, each of which alters the state variables (x0, x1, x2, x3, x4) differently, + /// ensuring that the transformation introduces non-linearity and diffusion, which are essential for cryptographic security. + ///

+ /// When is set to 12, the method first applies four unique round constants. + /// Afterward, it applies a fixed set of six additional constants regardless of the number of rounds. + ///
+ private void P(int numberOfRounds) + { + if (numberOfRounds == 12) + { + Round(0xf0UL); + Round(0xe1UL); + Round(0xd2UL); + Round(0xc3UL); + } + + Round(0xb4UL); + Round(0xa5UL); + + Round(0x96UL); + Round(0x87UL); + Round(0x78UL); + Round(0x69UL); + Round(0x5aUL); + Round(0x4bUL); + } + + /// + /// Executes a single round of the cryptographic permutation function, transforming the internal state + /// variables x0, x1, x2, x3, and x4 using XOR, AND, and NOT operations, along with circular bit rotations. + /// This function is designed to introduce diffusion and non-linearity into the state for cryptographic security. + /// + /// + /// A 64-bit unsigned integer constant that influences the round's transformation. Each round uses a unique value of this constant + /// to ensure that the transformation applied to the state differs for each round. + /// + /// + /// The Round function uses a series of bitwise operations (XOR, AND, NOT) and circular bit rotations to mix + /// the internal state. Each transformation step introduces non-linearity and ensures that small changes in the input or state + /// variables propagate widely across the internal state, enhancing the security of the cryptographic process. + ///

+ /// The round constant () plays a crucial role in altering the state at each round, ensuring + /// that each round contributes uniquely to the overall cryptographic transformation. Circular rotations are applied using + /// to spread bits throughout the 64-bit word. + ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Round(ulong circles) + { + // Step 1: Perform XOR and AND operations to mix inputs and state variables + var t0 = x0 ^ x1 ^ x2 ^ x3 ^ circles ^ (x1 & (x0 ^ x2 ^ x4 ^ circles)); + var t1 = x0 ^ x2 ^ x3 ^ x4 ^ circles ^ ((x1 ^ x2 ^ circles) & (x1 ^ x3)); + var t2 = x1 ^ x2 ^ x4 ^ circles ^ (x3 & x4); + var t3 = x0 ^ x1 ^ x2 ^ circles ^ (~x0 & (x3 ^ x4)); + var t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); + + // Step 2: Apply circular right shifts and update the internal state variables + x0 = t0 ^ LongUtils.RotateRight(t0, 19) ^ LongUtils.RotateRight(t0, 28); + x1 = t1 ^ LongUtils.RotateRight(t1, 39) ^ LongUtils.RotateRight(t1, 61); + x2 = ~(t2 ^ LongUtils.RotateRight(t2, 1) ^ LongUtils.RotateRight(t2, 6)); + x3 = t3 ^ LongUtils.RotateRight(t3, 10) ^ LongUtils.RotateRight(t3, 17); + x4 = t4 ^ LongUtils.RotateRight(t4, 7) ^ LongUtils.RotateRight(t4, 41); + } +} diff --git a/Algorithms/Crypto/Digests/IDigest.cs b/Algorithms/Crypto/Digests/IDigest.cs new file mode 100644 index 00000000..0800f176 --- /dev/null +++ b/Algorithms/Crypto/Digests/IDigest.cs @@ -0,0 +1,70 @@ +using System; + +namespace Algorithms.Crypto.Digests; + +/// +/// Interface for message digest algorithms, providing methods to update, finalize, and reset the digest state. +/// +public interface IDigest +{ + /// + /// Gets the name of the digest algorithm (e.g., "SHA-256"). + /// + string AlgorithmName { get; } + + /// + /// Gets the size of the digest in bytes (e.g., 32 bytes for SHA-256). + /// + /// The size of the digest in bytes. + int GetDigestSize(); + + /// + /// Gets the byte length of the internal buffer used by the digest. + /// + /// The byte length of the internal buffer. + int GetByteLength(); + + /// + /// Updates the digest with a single byte of input data. + /// + /// The byte to add to the digest. + void Update(byte input); + + /// + /// Updates the digest with a portion of a byte array. + /// + /// The byte array containing the input data. + /// The offset within the array to start reading from. + /// The length of data to read from the array. + void BlockUpdate(byte[] input, int inOff, int inLen); + + /// + /// Updates the digest with a portion of input data from a of bytes. + /// + /// The containing the input data. + void BlockUpdate(ReadOnlySpan input); + + /// + /// Completes the digest calculation and stores the result in the specified byte array. + /// + /// The byte array to store the final digest. + /// The offset within the array to start writing the digest. + /// The number of bytes written to the output array. + int DoFinal(byte[] output, int outOff); + + /// + /// Completes the digest calculation and stores the result in the specified of bytes. + /// + /// The to store the final digest. + /// The number of bytes written to the output span. + int DoFinal(Span output); + + string Digest(byte[] input); + + string Digest(Span input); + + /// + /// Resets the digest to its initial state, clearing all data accumulated so far. + /// + void Reset(); +} diff --git a/Algorithms/Crypto/Exceptions/CryptoException.cs b/Algorithms/Crypto/Exceptions/CryptoException.cs new file mode 100644 index 00000000..44b3bbd7 --- /dev/null +++ b/Algorithms/Crypto/Exceptions/CryptoException.cs @@ -0,0 +1,36 @@ +using System; + +namespace Algorithms.Crypto.Exceptions; + +/// +/// Represents errors that occur during cryptographic operations. +/// +public class CryptoException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + public CryptoException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public CryptoException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The message that describes the error. + /// The exception that is the cause of the current exception. + public CryptoException(string message, Exception inner) + : base(message, inner) + { + } +} diff --git a/Algorithms/Crypto/Exceptions/DataLengthException.cs b/Algorithms/Crypto/Exceptions/DataLengthException.cs new file mode 100644 index 00000000..f96e2a7f --- /dev/null +++ b/Algorithms/Crypto/Exceptions/DataLengthException.cs @@ -0,0 +1,36 @@ +using System; + +namespace Algorithms.Crypto.Exceptions; + +/// +/// Represents errors that occur when the length of data in a cryptographic operation is invalid or incorrect. +/// +public class DataLengthException : CryptoException +{ + /// + /// Initializes a new instance of the class. + /// + public DataLengthException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public DataLengthException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The message that describes the error. + /// The exception that is the cause of the current exception. + public DataLengthException(string message, Exception inner) + : base(message, inner) + { + } +} diff --git a/Algorithms/Crypto/Exceptions/OutputLengthException.cs b/Algorithms/Crypto/Exceptions/OutputLengthException.cs new file mode 100644 index 00000000..1e295fc4 --- /dev/null +++ b/Algorithms/Crypto/Exceptions/OutputLengthException.cs @@ -0,0 +1,57 @@ +using System; + +namespace Algorithms.Crypto.Exceptions; + +/// +/// Represents an exception that is thrown when the output buffer length is insufficient for a cryptographic operation. +/// +/// +/// The is a specific subclass of . It is used in cryptographic +/// operations to signal that the provided output buffer does not have enough space to store the required output. This exception is +/// typically thrown when encryption, hashing, or other cryptographic operations require more space than what has been allocated in +/// the output buffer. +///
+/// This exception provides constructors for creating the exception with a custom message, an inner exception, or both. By inheriting +/// from , it can be handled similarly in cases where both input and output length issues may arise. +///
+public class OutputLengthException : DataLengthException +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// This constructor initializes a new instance of the class without any additional message or inner exception. + /// It is commonly used when a generic output length issue needs to be raised without specific details. + /// + public OutputLengthException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + /// + /// This constructor allows for a custom error message to be provided, giving more detail about the specific issue with the output length. + /// + public OutputLengthException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The message that describes the error. + /// The exception that is the cause of the current exception. + /// + /// This constructor allows for both a custom message and an inner exception, which can be useful for propagating + /// the underlying cause of the error. For example, if the output buffer length is too short due to incorrect calculations, + /// the root cause (e.g., an ) can be passed in as the inner exception. + /// + public OutputLengthException(string message, Exception inner) + : base(message, inner) + { + } +} diff --git a/Algorithms/Crypto/Utils/ByteEncodingUtils.cs b/Algorithms/Crypto/Utils/ByteEncodingUtils.cs new file mode 100644 index 00000000..479d2508 --- /dev/null +++ b/Algorithms/Crypto/Utils/ByteEncodingUtils.cs @@ -0,0 +1,63 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace Algorithms.Crypto.Utils; + +/// +/// Provides utility methods for converting between byte arrays and 64-bit unsigned integers using big-endian byte order. +/// +/// +/// The class contains static methods that assist in reading and writing 64-bit unsigned integers +/// from and to byte arrays or spans in big-endian format. These methods are optimized for cryptographic operations where byte +/// encoding is critical for consistency and security. +/// +public static class ByteEncodingUtils +{ + /// + /// Converts an 8-byte segment from a byte array (starting at the specified offset) into a 64-bit unsigned integer using big-endian format. + /// + /// The byte array containing the input data. + /// The offset within the byte array to start reading from. + /// A 64-bit unsigned integer representing the big-endian interpretation of the byte array segment. + /// Thrown if the specified offset is out of range of the byte array. + /// + /// This method reads 8 bytes from the specified offset within the byte array and converts them to a 64-bit unsigned integer + /// in big-endian format. Big-endian format stores the most significant byte first, followed by the less significant bytes. + /// + public static ulong BigEndianToUint64(byte[] byteStream, int offset) + { + return BinaryPrimitives.ReadUInt64BigEndian(byteStream.AsSpan(offset)); + } + + /// + /// Converts a read-only span of bytes into a 64-bit unsigned integer using big-endian format. + /// + /// A read-only span containing the input data. + /// A 64-bit unsigned integer representing the big-endian interpretation of the span of bytes. + /// + /// This method is optimized for performance using the attribute to encourage + /// inlining by the compiler. It reads exactly 8 bytes from the input span and converts them into a 64-bit unsigned integer. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong BigEndianToUint64(ReadOnlySpan byteStream) + { + return BinaryPrimitives.ReadUInt64BigEndian(byteStream); + } + + /// + /// Writes a 64-bit unsigned integer to a span of bytes using big-endian format. + /// + /// The 64-bit unsigned integer to write. + /// The span of bytes where the value will be written. + /// + /// This method writes the 64-bit unsigned integer into the span in big-endian format, where the most significant byte is written first. + /// The method is optimized using the attribute to improve performance in scenarios + /// where frequent byte-to-integer conversions are required, such as cryptographic algorithms. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UInt64ToBigEndian(ulong value, Span byteStream) + { + BinaryPrimitives.WriteUInt64BigEndian(byteStream, value); + } +} diff --git a/Algorithms/Crypto/Utils/LongUtils.cs b/Algorithms/Crypto/Utils/LongUtils.cs new file mode 100644 index 00000000..60f266f8 --- /dev/null +++ b/Algorithms/Crypto/Utils/LongUtils.cs @@ -0,0 +1,78 @@ +using System.Numerics; + +namespace Algorithms.Crypto.Utils; + +/// +/// Provides utility methods for performing bitwise rotation operations (left and right) on 64-bit integers. +/// +/// +/// The class contains methods to rotate 64-bit signed and unsigned integers to the left or right. +/// These rotations are crucial in various cryptographic algorithms, where circular shifts are used to mix data and +/// introduce non-linearity. The methods use the underlying for efficient, +/// hardware-supported bitwise rotations. +/// +public static class LongUtils +{ + /// + /// Rotates the bits of a 64-bit signed integer to the left by a specified number of bits. + /// + /// The 64-bit signed integer to rotate. + /// The number of bits to rotate the integer to the left. + /// The result of rotating the integer to the left by the specified distance. + /// + /// This method uses the underlying method, converting the signed integer to an unsigned integer + /// for the rotation, then casting it back to a signed integer. The rotation is performed in a circular manner, where bits shifted + /// out of the most significant bit are reintroduced into the least significant bit. + /// + public static long RotateLeft(long i, int distance) + { + return (long)BitOperations.RotateLeft((ulong)i, distance); + } + + /// + /// Rotates the bits of a 64-bit unsigned integer to the left by a specified number of bits. + /// + /// The 64-bit unsigned integer to rotate. + /// The number of bits to rotate the integer to the left. + /// The result of rotating the integer to the left by the specified distance. + /// + /// The rotation is performed circularly, meaning bits shifted out of the most significant bit are reintroduced into + /// the least significant bit. This method is optimized for performance using hardware-supported operations through + /// . + /// + public static ulong RotateLeft(ulong i, int distance) + { + return BitOperations.RotateLeft(i, distance); + } + + /// + /// Rotates the bits of a 64-bit signed integer to the right by a specified number of bits. + /// + /// The 64-bit signed integer to rotate. + /// The number of bits to rotate the integer to the right. + /// The result of rotating the integer to the right by the specified distance. + /// + /// Similar to the left rotation, this method uses to perform the rotation. + /// The signed integer is cast to an unsigned integer for the operation and cast back to a signed integer afterward. + /// The rotation wraps bits shifted out of the least significant bit into the most significant bit. + /// + public static long RotateRight(long i, int distance) + { + return (long)BitOperations.RotateRight((ulong)i, distance); + } + + /// + /// Rotates the bits of a 64-bit unsigned integer to the right by a specified number of bits. + /// + /// The 64-bit unsigned integer to rotate. + /// The number of bits to rotate the integer to the right. + /// The result of rotating the integer to the right by the specified distance. + /// + /// This method performs the rotation circularly, where bits shifted out of the least significant bit are reintroduced + /// into the most significant bit. The operation uses hardware-supported instructions via . + /// + public static ulong RotateRight(ulong i, int distance) + { + return BitOperations.RotateRight(i, distance); + } +} diff --git a/Algorithms/Crypto/Utils/ValidationUtils.cs b/Algorithms/Crypto/Utils/ValidationUtils.cs new file mode 100644 index 00000000..88931dfa --- /dev/null +++ b/Algorithms/Crypto/Utils/ValidationUtils.cs @@ -0,0 +1,95 @@ +using System; +using System.Diagnostics; +using Algorithms.Crypto.Exceptions; + +namespace Algorithms.Crypto.Utils; + +/// +/// Provides utility methods for validating the lengths of input and output data in cryptographic operations. +/// +/// +/// The class contains static methods to validate the length and position of data buffers used in +/// cryptographic operations. These methods throw appropriate exceptions such as or +/// when the validation fails. These are critical for ensuring that cryptographic computations +/// do not run into buffer overflows, underflows, or incorrect input/output buffer lengths. +/// +public static class ValidationUtils +{ + /// + /// Validates that the specified offset and length fit within the bounds of the given buffer. + /// + /// The byte array to validate. + /// The offset into the byte array where validation should start. + /// The number of bytes to validate from the specified offset. + /// The message that describes the error if the exception is thrown. + /// Thrown if the offset and length exceed the bounds of the buffer. + /// + /// This method ensures that the specified offset and length fit within the bounds of the buffer. If the offset and length + /// go out of bounds, a is thrown with the provided error message. + /// + public static void CheckDataLength(byte[] buffer, int offset, int length, string message) + { + if (offset > (buffer.Length - length)) + { + throw new DataLengthException(message); + } + } + + /// + /// Throws an if the specified condition is true. + /// + /// A boolean condition indicating whether the exception should be thrown. + /// The message that describes the error if the exception is thrown. + /// Thrown if the condition is true. + /// + /// This method performs a simple conditional check for output length validation. If the condition is true, an + /// is thrown with the provided message. + /// + public static void CheckOutputLength(bool condition, string message) + { + if (condition) + { + throw new OutputLengthException(message); + } + } + + /// + /// Validates that the specified offset and length fit within the bounds of the output buffer. + /// + /// The byte array to validate. + /// The offset into the byte array where validation should start. + /// The number of bytes to validate from the specified offset. + /// The message that describes the error if the exception is thrown. + /// Thrown if the offset and length exceed the bounds of the buffer. + /// + /// This method ensures that the specified offset and length do not exceed the bounds of the output buffer. If the + /// validation fails, an is thrown with the provided message. + /// + public static void CheckOutputLength(byte[] buffer, int offset, int length, string message) + { + if (offset > (buffer.Length - length)) + { + throw new OutputLengthException(message); + } + } + + /// + /// Validates that the length of the output span does not exceed the specified length. + /// + /// The type of elements in the span. + /// The span to validate. + /// The maximum allowed length for the output span. + /// The message that describes the error if the exception is thrown. + /// Thrown if the length of the output span exceeds the specified length. + /// + /// This method checks that the span does not exceed the specified length. If the span length exceeds the allowed length, + /// an is thrown with the provided error message. + /// + public static void CheckOutputLength(Span output, int length, string message) + { + if (output.Length > length) + { + throw new OutputLengthException(message); + } + } +} diff --git a/README.md b/README.md index f13516de..93e048d2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ find more than one implementation for the same objective but using different alg * [TBC Padding](./Algorithms/Crypto/Paddings/TbcPadding.cs) * [PKCS7 Padding](./Algorithms/Crypto/Paddings/Pkcs7Padding.cs) * [Digests](./Algorithms/Crypto/Digests/) + * [Ascon Hash Digest](./Algorithms/Crypto/Digests/AsconDigest.cs) * [MD2 Digest](./Algorithms/Crypto/Digests/Md2Digest.cs) * [Data Compression](./Algorithms/DataCompression) * [Burrows-Wheeler transform](./Algorithms/DataCompression/BurrowsWheelerTransform.cs) @@ -66,7 +67,7 @@ find more than one implementation for the same objective but using different alg * [Extended Euclidean Algorithm](./Algorithms/ModularArithmetic/ExtendedEuclideanAlgorithm.cs) * [Modular Multiplicative Inverse](./Algorithms/ModularArithmetic/ModularMultiplicativeInverse.cs) * [Numeric](./Algorithms/Numeric) - * [Absolute](./Algorithms/Numeric/Abs.cs) + * [Absolute](./Algorithms/Numeric/Abs.cs) * [Aliquot Sum Calculator](./Algorithms/Numeric/AliquotSumCalculator.cs) * [Amicable Numbers Checker](./Algorithms/Numeric/AmicableNumbersChecker.cs) * [Decomposition](./Algorithms/Numeric/Decomposition)