diff --git a/.editorconfig b/.editorconfig index df8f9a8..1c42688 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,13 @@ root = true [*] -max_line_length = 140 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 +continuation_indent_size = 4 +max_line_length = 140 + +# ReSharper properties +resharper_csharp_wrap_lines = false diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index fdee6ea..32f8ed7 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Restore dependencies run: dotnet restore - name: Build diff --git a/.idea/.idea.onixlabs-dotnet/.idea/vcs.xml b/.idea/.idea.onixlabs-dotnet/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/.idea.onixlabs-dotnet/.idea/vcs.xml +++ b/.idea/.idea.onixlabs-dotnet/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.run/Build & Test.run.xml b/.run/Build & Test.run.xml new file mode 100644 index 0000000..35305ea --- /dev/null +++ b/.run/Build & Test.run.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.run/Run Playground.run.xml b/.run/Run Playground.run.xml new file mode 100644 index 0000000..611820d --- /dev/null +++ b/.run/Run Playground.run.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/OnixLabs.Core.UnitTests/MockData/Color.cs b/OnixLabs.Core.UnitTests.Data/Color.cs similarity index 90% rename from OnixLabs.Core.UnitTests/MockData/Color.cs rename to OnixLabs.Core.UnitTests.Data/Color.cs index 891e675..ec7f714 100644 --- a/OnixLabs.Core.UnitTests/MockData/Color.cs +++ b/OnixLabs.Core.UnitTests.Data/Color.cs @@ -1,18 +1,18 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -namespace OnixLabs.Core.UnitTests.MockData; +namespace OnixLabs.Core.UnitTests.Data; public sealed class Color : Enumeration { diff --git a/OnixLabs.Core.UnitTests.Data/Element.cs b/OnixLabs.Core.UnitTests.Data/Element.cs new file mode 100644 index 0000000..1066640 --- /dev/null +++ b/OnixLabs.Core.UnitTests.Data/Element.cs @@ -0,0 +1,23 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Core.UnitTests.Data; + +public sealed class Element(int hashCode = 0) +{ + public bool Called { get; set; } + private int HashCode { get; } = hashCode; + + public override int GetHashCode() => HashCode; +} diff --git a/OnixLabs.Core.UnitTests.Data/Numeric.cs b/OnixLabs.Core.UnitTests.Data/Numeric.cs new file mode 100644 index 0000000..dc20316 --- /dev/null +++ b/OnixLabs.Core.UnitTests.Data/Numeric.cs @@ -0,0 +1,30 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Core.UnitTests.Data; + +public sealed class Numeric(T value) : IEquatable> where T : INumber +{ + public T Value { get; } = value; + + public bool Equals(Numeric? other) => other is not null && other.Value == Value; + + public override bool Equals(object? obj) => obj is Numeric other && Equals(other); + + public override int GetHashCode() => HashCode.Combine(Value); + + public override string ToString() => this.ToRecordString(); +} diff --git a/OnixLabs.Core.UnitTests.Data/OnixLabs.Core.UnitTests.Data.csproj b/OnixLabs.Core.UnitTests.Data/OnixLabs.Core.UnitTests.Data.csproj new file mode 100644 index 0000000..da413fb --- /dev/null +++ b/OnixLabs.Core.UnitTests.Data/OnixLabs.Core.UnitTests.Data.csproj @@ -0,0 +1,29 @@ + + + net8.0 + enable + enable + + false + + 12 + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/OnixLabs.Core.UnitTests.Data/Record.cs b/OnixLabs.Core.UnitTests.Data/Record.cs new file mode 100644 index 0000000..ff6927a --- /dev/null +++ b/OnixLabs.Core.UnitTests.Data/Record.cs @@ -0,0 +1,17 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Core.UnitTests.Data; + +public sealed record Record(string Text, int Number, T Value); diff --git a/OnixLabs.Core.UnitTests/ArrayExtensionTests.cs b/OnixLabs.Core.UnitTests/ArrayExtensionTests.cs new file mode 100644 index 0000000..e53e0df --- /dev/null +++ b/OnixLabs.Core.UnitTests/ArrayExtensionTests.cs @@ -0,0 +1,65 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Core.UnitTests; + +public sealed class ArrayExtensionTests +{ + [Fact(DisplayName = "Array.Copy should produce a copy of an array")] + public void CopyShouldProduceExpectedResult() + { + // Given + int[] array = [1, 2, 3, 4, 5]; + int[] expected = [1, 2, 3, 4, 5]; + + // When + int[] actual = array.Copy(); + + // Then + Assert.Equal(expected, actual); + Assert.False(ReferenceEquals(array, actual)); + } + + [Fact(DisplayName = "Array.Copy with index and count parameters should produce a copy of an array")] + public void CopyWithParametersShouldProduceExpectedResult() + { + // Given + int[] array = [1, 2, 3, 4, 5]; + int[] expected = [3, 4, 5]; + + // When + int[] actual = array.Copy(2, 3); + + // Then + Assert.Equal(expected, actual); + Assert.False(ReferenceEquals(array, actual)); + } + + [Fact(DisplayName = "Array.ConcatenateWith should produce a concatenation of two arrays")] + public void ConcatenateWithShouldProduceExpectedResult() + { + // Given + int[] left = [1, 2, 3, 4, 5]; + int[] right = [6, 7, 8, 9, 10]; + int[] expected = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + // When + int[] actual = left.ConcatenateWith(right); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/EnumerationTests.cs b/OnixLabs.Core.UnitTests/EnumerationTests.cs index 4d3dfd2..5ed15a8 100644 --- a/OnixLabs.Core.UnitTests/EnumerationTests.cs +++ b/OnixLabs.Core.UnitTests/EnumerationTests.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,7 +13,7 @@ // limitations under the License. using System.Collections.Generic; -using OnixLabs.Core.UnitTests.MockData; +using OnixLabs.Core.UnitTests.Data; using Xunit; namespace OnixLabs.Core.UnitTests; @@ -23,32 +23,32 @@ public sealed class EnumerationTests [Fact(DisplayName = "Enumerations should be equal")] public void EnumerationsShouldBeEqual() { - // Arrange + // Given Color a = Color.Red; Color b = Color.Red; - // Assert + // Then Assert.Equal(a, b); } [Fact(DisplayName = "Enumerations should not be equal")] public void EnumerationsShouldNotBeEqual() { - // Arrange + // Given Color a = Color.Red; Color b = Color.Blue; - // Assert + // Then Assert.NotEqual(a, b); } [Fact(DisplayName = "Enumeration should return all enumeration instances")] public void EnumerationsShouldReturnAllEnumerationInstances() { - // Arrange + // Given IEnumerable colors = Color.GetAll(); - // Assert + // Then Assert.Contains(colors, item => item == Color.Red); Assert.Contains(colors, item => item == Color.Green); Assert.Contains(colors, item => item == Color.Blue); @@ -57,30 +57,30 @@ public void EnumerationsShouldReturnAllEnumerationInstances() [Fact(DisplayName = "Enumeration.FromName should return the expected enumeration entry")] public void EnumerationFromNameShouldReturnTheExpectedEnumerationEntry() { - // Arrange + // Given Color color = Color.FromName("Green"); - // Assert + // Then Assert.Equal(Color.Green, color); } [Fact(DisplayName = "Enumeration.FromValue should return the expected enumeration entry")] public void EnumerationFromValueShouldReturnTheExpectedEnumerationEntry() { - // Arrange + // Given Color color = Color.FromValue(2); - // Assert + // Then Assert.Equal(Color.Green, color); } [Fact(DisplayName = "Enumeration.GetAll should return all enumeration entries")] public void EnumerationGetAllShouldReturnAllEnumerationEntries() { - // Arrange + // Given IEnumerable entries = Color.GetAll(); - // Assert + // Then Assert.Contains(Color.Blue, entries); Assert.Contains(Color.Green, entries); Assert.Contains(Color.Red, entries); @@ -89,10 +89,10 @@ public void EnumerationGetAllShouldReturnAllEnumerationEntries() [Fact(DisplayName = "Enumeration.GetEntries should return all enumeration entries")] public void EnumerationGetEntriesShouldReturnAllEnumerationEntries() { - // Arrange + // Given IEnumerable<(int Value, string Name)> entries = Color.GetEntries(); - // Assert + // Then Assert.Contains(Color.Blue.ToEntry(), entries); Assert.Contains(Color.Green.ToEntry(), entries); Assert.Contains(Color.Red.ToEntry(), entries); @@ -101,10 +101,10 @@ public void EnumerationGetEntriesShouldReturnAllEnumerationEntries() [Fact(DisplayName = "Enumeration.GetNames should return all enumeration names")] public void EnumerationGetNamesShouldReturnAllEnumerationNames() { - // Arrange + // Given IEnumerable entries = Color.GetNames(); - // Assert + // Then Assert.Contains(Color.Blue.Name, entries); Assert.Contains(Color.Green.Name, entries); Assert.Contains(Color.Red.Name, entries); @@ -113,12 +113,40 @@ public void EnumerationGetNamesShouldReturnAllEnumerationNames() [Fact(DisplayName = "Enumeration.GetValues should return all enumeration values")] public void EnumerationGetValuesShouldReturnAllEnumerationValues() { - // Arrange + // Given IEnumerable entries = Color.GetValues(); - // Assert + // Then Assert.Contains(Color.Blue.Value, entries); Assert.Contains(Color.Green.Value, entries); Assert.Contains(Color.Red.Value, entries); } -} \ No newline at end of file + + [Fact(DisplayName = "Enumeration.CompareTo as Enumeration should return the correct value")] + public void EnumerationCompareToAsEnumerationShouldReturnTheCorrectValue() + { + // Given + Color left = Color.Red; + Color right = Color.Blue; + + // When + int actual = left.CompareTo(right); + + // Then + Assert.Equal(-1, actual); + } + + [Fact(DisplayName = "Enumeration.CompareTo as Object should return the correct value")] + public void EnumerationCompareToAsObjectShouldReturnTheCorrectValue() + { + // Given + Color left = Color.Red; + object right = Color.Blue; + + // When + int actual = left.CompareTo(right); + + // Then + Assert.Equal(-1, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Linq/IEnumerableExtensionTests.cs b/OnixLabs.Core.UnitTests/Linq/IEnumerableExtensionTests.cs new file mode 100644 index 0000000..84280fe --- /dev/null +++ b/OnixLabs.Core.UnitTests/Linq/IEnumerableExtensionTests.cs @@ -0,0 +1,490 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using OnixLabs.Core.Linq; +using OnixLabs.Core.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Linq; + +// ReSharper disable InconsistentNaming +public sealed class IEnumerableExtensionTests +{ + [Fact(DisplayName = "IEnumerable.AllEqualBy should return true when all items are equal by the same property")] + public void AllEqualByShouldProduceExpectedResultTrue() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("abc", 123, Guid.NewGuid()); + Record element3 = new("abc", 123, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + + // When + bool result = elements.AllEqualBy(element => element.Text); + + // Then + Assert.True(result); + } + + [Fact(DisplayName = "IEnumerable.AllEqualBy should return false when all items are not equal by the same property")] + public void AllEqualByShouldProduceExpectedResultFalse() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("abc", 123, Guid.NewGuid()); + Record element3 = new("xyz", 123, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + + // When + bool result = elements.AllEqualBy(element => element.Text); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.AnyEqualBy should return true when any items are equal by the same property")] + public void AnyEqualByShouldProduceExpectedResultTrue() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("abc", 123, Guid.NewGuid()); + Record element3 = new("xyz", 123, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + + // When + bool result = elements.AnyEqualBy(element => element.Text); + + // Then + Assert.True(result); + } + + [Fact(DisplayName = "IEnumerable.AnyEqualBy should return false when any items are not equal by the same property")] + public void AnyEqualByShouldProduceExpectedResultFalse() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("def", 123, Guid.NewGuid()); + Record element3 = new("xyz", 123, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + + // When + bool result = elements.AnyEqualBy(element => element.Text); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.CountNot should produce the expected result.")] + public void CountNotShouldProduceExpectedResult() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("def", 123, Guid.NewGuid()); + Record element3 = new("xyz", 456, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + const int expected = 2; + + // When + int actual = elements.CountNot(element => element.Number == 456); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.ForEach should iterate over every element in the enumerable")] + public void ForEachShouldProduceExpectedResult() + { + // Given + IEnumerable enumerable = new[] { new Element(), new Element(), new Element() }; + + // When + enumerable.ForEach(element => element.Called = true); + + // Then + Assert.All(enumerable, element => Assert.True(element.Called)); + } + + [Fact(DisplayName = "IEnumerable.GetContentHashCode should produce equal hash codes")] + public void GetContentHashCodeShouldProduceExpectedResultEqual() + { + // Given + IEnumerable enumerable1 = new[] { new Element(1), new Element(2), new Element(3) }; + IEnumerable enumerable2 = new[] { new Element(1), new Element(2), new Element(3) }; + + // When + int hashCode1 = enumerable1.GetContentHashCode(); + int hashCode2 = enumerable2.GetContentHashCode(); + + // Then + Assert.Equal(hashCode1, hashCode2); + } + + [Fact(DisplayName = "IEnumerable.GetContentHashCode should produce different hash codes")] + public void GetContentHashCodeShouldProduceExpectedResultDifferent() + { + // Given + IEnumerable enumerable1 = new[] { new Element(1), new Element(2), new Element(3) }; + IEnumerable enumerable2 = new[] { new Element(3), new Element(2), new Element(1) }; + + // When + int hashCode1 = enumerable1.GetContentHashCode(); + int hashCode2 = enumerable2.GetContentHashCode(); + + // Then + Assert.NotEqual(hashCode1, hashCode2); + } + + [Fact(DisplayName = "IEnumerable.IsEmpty should return true when the enumerable is empty")] + public void IsEmptyShouldProduceExpectedResultTrue() + { + // Given + IEnumerable enumerable = Enumerable.Empty(); + + // When + bool result = enumerable.IsEmpty(); + + // Then + Assert.True(result); + } + + [Fact(DisplayName = "IEnumerable.IsEmpty should return false when the enumerable is not empty")] + public void IsEmptyShouldProduceExpectedResultFalse() + { + // Given + IEnumerable enumerable = new[] { new Element() }; + + // When + bool result = enumerable.IsEmpty(); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.IsNotEmpty should return true when the enumerable is not empty")] + public void IsNotEmptyShouldProduceExpectedResultTrue() + { + // Given + IEnumerable enumerable = new[] { new Element() }; + + // When + bool result = enumerable.IsNotEmpty(); + + // Then + Assert.True(result); + } + + [Fact(DisplayName = "IEnumerable.IsNotEmpty should return false when the enumerable is empty")] + public void IsNotEmptyShouldProduceExpectedResultFalse() + { + // Given + IEnumerable enumerable = Enumerable.Empty(); + + // When + bool result = enumerable.IsNotEmpty(); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.IsSingle should return true when the enumerable contains a single element")] + public void IsSingleShouldProduceExpectedResultTrue() + { + // Given + IEnumerable enumerable = new[] { new Element() }; + + // When + bool result = enumerable.IsSingle(); + + // Then + Assert.True(result); + } + + [Fact(DisplayName = "IEnumerable.IsSingle should return false when the enumerable is empty")] + public void IsSingleShouldProduceExpectedResultFalseWhenEmpty() + { + // Given + IEnumerable enumerable = Enumerable.Empty(); + + // When + bool result = enumerable.IsSingle(); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.IsSingle should return false when the enumerable contains more than one element")] + public void IsSingleShouldProduceExpectedResultFalseWhenMoreThanOneElement() + { + // Given + IEnumerable enumerable = new[] { new Element(), new Element() }; + + // When + bool result = enumerable.IsSingle(); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.IsCountEven should return true when the enumerable contains an even number of elements")] + public void IsCountEvenShouldProduceExpectedResultTrue() + { + // Given + IEnumerable enumerable = new[] { new Element(), new Element() }; + + // When + bool result = enumerable.IsCountEven(); + + // Then + Assert.True(result); + } + + [Fact(DisplayName = "IEnumerable.IsCountEven should return false when the enumerable contains an odd number of elements")] + public void IsCountEvenShouldProduceExpectedResultFalse() + { + // Given + IEnumerable enumerable = new[] { new Element() }; + + // When + bool result = enumerable.IsCountEven(); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.IsCountOdd should return true when the enumerable contains an odd number of elements")] + public void IsCountOddShouldProduceExpectedResultTrue() + { + // Given + IEnumerable enumerable = new[] { new Element() }; + + // When + bool result = enumerable.IsCountOdd(); + + // Then + Assert.True(result); + } + + [Fact(DisplayName = "IEnumerable.IsCountOdd should return false when the enumerable contains an even number of elements")] + public void IsCountOddShouldProduceExpectedResultFalse() + { + // Given + IEnumerable enumerable = new[] { new Element(), new Element() }; + + // When + bool result = enumerable.IsCountOdd(); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.JoinToString should produce the expected result with the default separator")] + public void JoinToStringShouldProduceExpectedResultWithDefaultSeparator() + { + // Given + IEnumerable enumerable = new object[] { 1, 2, 3, 4.5, true, false }; + const string expected = "1, 2, 3, 4.5, True, False"; + + // When + string actual = enumerable.JoinToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.JoinToString should produce the expected result with a custom separator")] + public void JoinToStringShouldProduceExpectedResultWithCustomSeparator() + { + // Given + IEnumerable enumerable = new object[] { 1, 2, 3, 4.5, true, false }; + const string expected = "1 *$ 2 *$ 3 *$ 4.5 *$ True *$ False"; + + // When + string actual = enumerable.JoinToString(" *$ "); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.None should return true when none of the elements satisfy the specified predicate condition")] + public void NoneShouldProduceExpectedResultTrue() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("def", 456, Guid.NewGuid()); + Record element3 = new("xyz", 789, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + + // When + bool result = elements.None(element => element.Number == 0); + + // Then + Assert.True(result); + } + + [Fact(DisplayName = "IEnumerable.None should return false when any of the elements satisfy the specified predicate condition")] + public void NoneShouldProduceExpectedResultFalseAny() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("def", 456, Guid.NewGuid()); + Record element3 = new("xyz", 0, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + + // When + bool result = elements.None(element => element.Number == 0); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.None should return false when all of the elements satisfy the specified predicate condition")] + public void NoneShouldProduceExpectedResultFalseAll() + { + // Given + Record element1 = new("abc", 0, Guid.NewGuid()); + Record element2 = new("def", 0, Guid.NewGuid()); + Record element3 = new("xyz", 0, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + + // When + bool result = elements.None(element => element.Number == 0); + + // Then + Assert.False(result); + } + + [Fact(DisplayName = "IEnumerable.Sum should produce the expected result")] + public void SumShouldProduceExpectedResult() + { + // Given + IEnumerable elements = [12.34m, 34.56m, 56.78m]; + const decimal expected = 103.68m; + + // When + decimal actual = elements.Sum(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.Sum with selector should produce the expected result")] + public void SumWithSelectorShouldProduceExpectedResult() + { + // Given + Numeric element1 = new(1234.567m); + Numeric element2 = new(890.1234m); + Numeric element3 = new(56.78901m); + IEnumerable> elements = [element1, element2, element3]; + const decimal expected = 2181.47941m; + + // When + decimal actual = elements.SumBy(element => element.Value); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.WhereNot should produce the expected result")] + public void WhereNotShouldProduceExpectedResult() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("def", 456, Guid.NewGuid()); + Record element3 = new("xyz", 789, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, element3 }; + IEnumerable> expected = new[] { element2, element3 }; + + // When + IEnumerable> actual = elements.WhereNot(element => element.Number == 123); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.WhereNotNull should produce the expected result")] + public void WhereNotNullShouldProduceExpectedResult() + { + // Given + Record element1 = new("abc", 123, Guid.NewGuid()); + Record element2 = new("def", 456, Guid.NewGuid()); + IEnumerable> elements = new[] { element1, element2, null }; + IEnumerable> expected = new[] { element1, element2 }; + + // When + IEnumerable> actual = elements.WhereNotNull(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.ToCollectionString should produce expected result (Object)")] + public void ToCollectionStringShouldProduceExpectedResultObject() + { + // Given + object[] values = [123, "abc", true, 123.456]; + const string expected = "[123, abc, True, 123.456]"; + + // When + string actual = values.ToCollectionString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.ToCollectionString should produce expected result (String)")] + public void ToCollectionStringShouldProduceExpectedResultString() + { + // Given + string[] values = ["abc", "xyz", "123"]; + const string expected = "[abc, xyz, 123]"; + + // When + string actual = values.ToCollectionString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.ToCollectionString should produce expected result (Int32)")] + public void ToCollectionStringShouldProduceExpectedResultInt32() + { + // Given + int[] values = [0, 1, 12, 123, 1234, -1, -12, -123, -1234]; + const string expected = "[0, 1, 12, 123, 1234, -1, -12, -123, -1234]"; + + // When + string actual = values.ToCollectionString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IEnumerable.ToCollectionString should produce expected result (Int32, IFormattable)")] + public void ToCollectionStringShouldProduceExpectedResultInt32IFormattable() + { + // Given + int[] values = [0, 1, 12, 123, 1234, -1, -12, -123, -1234]; + const string expected = "[£0.00, £1.00, £12.00, £123.00, £1,234.00, -£1.00, -£12.00, -£123.00, -£1,234.00]"; + + // When + string actual = values.ToCollectionString("C", CultureInfo.GetCultureInfo("en-GB")); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/ObjectExtensionTests.cs b/OnixLabs.Core.UnitTests/ObjectExtensionTests.cs new file mode 100644 index 0000000..4492516 --- /dev/null +++ b/OnixLabs.Core.UnitTests/ObjectExtensionTests.cs @@ -0,0 +1,36 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using OnixLabs.Core.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Core.UnitTests; + +public sealed class ObjectExtensionTests +{ + [Fact(DisplayName = "Object.ToRecordString should produce a record formatted string")] + public void ToRecordStringShouldProduceExpectedResult() + { + // Given + Record record = new("abc", 123, Guid.NewGuid()); + string expected = record.ToString(); + + // When + string actual = record.ToRecordString(); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/OnixLabs.Core.UnitTests.csproj b/OnixLabs.Core.UnitTests/OnixLabs.Core.UnitTests.csproj index 0ec43ac..2a9f528 100644 --- a/OnixLabs.Core.UnitTests/OnixLabs.Core.UnitTests.csproj +++ b/OnixLabs.Core.UnitTests/OnixLabs.Core.UnitTests.csproj @@ -1,28 +1,26 @@ - - net6.0 - + net8.0 false - - 10 + 12 - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + + + + - diff --git a/OnixLabs.Core.UnitTests/PreconditionTests.cs b/OnixLabs.Core.UnitTests/PreconditionTests.cs index 8331dc2..0cb7b4b 100644 --- a/OnixLabs.Core.UnitTests/PreconditionTests.cs +++ b/OnixLabs.Core.UnitTests/PreconditionTests.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,21 +14,58 @@ using System; using Xunit; -using static OnixLabs.Core.Preconditions; namespace OnixLabs.Core.UnitTests; public sealed class PreconditionTests { [Fact(DisplayName = "Check should throw an ArgumentException when the condition is false")] - public void CheckShouldThrowAnArgumentExceptionWhenTheConditionIsFalse() + public void CheckShouldProduceExpectedResult() { - Assert.Throws(() => Check(false)); + // When + InvalidOperationException exception = Assert.Throws(() => Check(false)); + + // Then + Assert.Equal("Check failed.", exception.Message); } [Fact(DisplayName = "CheckNotNull should throw an ArgumentNullException when the condition is null")] - public void CheckNotNullShouldThrowAnArgumentNullExceptionWhenTheConditionIsNull() + public void CheckNotNullShouldProduceExpectedResult() + { + // When + InvalidOperationException exception = Assert.Throws(() => CheckNotNull(null)); + + // Then + Assert.Equal("Argument must not be null.", exception.Message); + } + + [Fact(DisplayName = "Require should throw an ArgumentException when the condition is false")] + public void RequireShouldProduceExpectedResult() + { + // When + ArgumentException exception = Assert.Throws(() => Require(false)); + + // Then + Assert.Equal("Argument requirement failed.", exception.Message); + } + + [Fact(DisplayName = "RequireWithinRange should throw an ArgumentOutOfRangeException when the condition is false")] + public void RequireWithinRangeShouldProduceExpectedResult() { - Assert.Throws(() => CheckNotNull(null)); + // When + ArgumentOutOfRangeException exception = Assert.Throws(() => RequireWithinRange(false)); + + // Then + Assert.Equal("Argument is out of range.", exception.Message); + } + + [Fact(DisplayName = "RequireNotNull should throw an ArgumentNullException when the condition is null")] + public void RequireNotNullShouldProduceExpectedResult() + { + // When + ArgumentNullException exception = Assert.Throws(() => RequireNotNull(null)); + + // Then + Assert.Equal("Argument must not be null.", exception.Message); } } diff --git a/OnixLabs.Core.UnitTests/Reflection/TypeExtensionTests.cs b/OnixLabs.Core.UnitTests/Reflection/TypeExtensionTests.cs new file mode 100644 index 0000000..063e94d --- /dev/null +++ b/OnixLabs.Core.UnitTests/Reflection/TypeExtensionTests.cs @@ -0,0 +1,36 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using OnixLabs.Core.Reflection; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Reflection; + +public sealed class TypeExtensionTests +{ + [Theory(DisplayName = "Type.GetName should produce the expected result")] + [InlineData(typeof(object), "Object")] + [InlineData(typeof(List<>), "List")] + [InlineData(typeof(Dictionary<,>), "Dictionary")] + public void TypeGetNameShouldProduceExpectedResult(Type type, string expected) + { + // When + string actual = type.GetName(); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/StringExtensionTests.cs b/OnixLabs.Core.UnitTests/StringExtensionTests.cs index 2908b43..aea70b5 100644 --- a/OnixLabs.Core.UnitTests/StringExtensionTests.cs +++ b/OnixLabs.Core.UnitTests/StringExtensionTests.cs @@ -1,139 +1,202 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.Globalization; using Xunit; namespace OnixLabs.Core.UnitTests; public sealed class StringExtensionTests { - [Theory(DisplayName = "SubstringBefore should return the expected result (char)")] + [Theory(DisplayName = "String.Repeat should return the expected result")] + [InlineData("0", 10, "0000000000")] + [InlineData("Abc1", 3, "Abc1Abc1Abc1")] + public void RepeatShouldProduceExpectedResult(string value, int count, string expected) + { + // When + string actual = value.Repeat(count); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "String.SubstringBeforeFirst should return the string before the first delimiter")] [InlineData("First:Second", "First", ':')] [InlineData("12345+678910", "12345", '+')] - public void SubstringBeforeShouldReturnTheExpectedResultChar(string value, string expected, char delimiter) + public void SubstringBeforeFirstShouldProduceExpectedResultChar(string value, string expected, char delimiter) { - // Arrange / Act - string actual = value.SubstringBefore(delimiter); + // When + string actual = value.SubstringBeforeFirst(delimiter); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "SubstringBefore should return the expected result (string)")] + [Theory(DisplayName = "String.SubstringBeforeFirst should return the string before the first delimiter")] [InlineData("First:Second", "First", ":")] [InlineData("12345+678910", "12345", "+")] - public void SubstringBeforeShouldReturnTheExpectedResultString(string value, string expected, string delimiter) + public void SubstringBeforeFirstShouldProduceExpectedResultString(string value, string expected, string delimiter) { - // Arrange / Act - string actual = value.SubstringBefore(delimiter); + // When + string actual = value.SubstringBeforeFirst(delimiter); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "SubstringBeforeLast should return the expected result (char)")] + [Theory(DisplayName = "String.SubstringBeforeLast should return the string before the last char delimiter")] [InlineData("First:Second:Third", "First:Second", ':')] [InlineData("12345+678910+12345", "12345+678910", '+')] - public void SubstringBeforeLastShouldReturnTheExpectedResultChar(string value, string expected, char delimiter) + public void SubstringBeforeLastShouldProduceExpectedResultChar(string value, string expected, char delimiter) { - // Arrange / Act + // When string actual = value.SubstringBeforeLast(delimiter); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "SubstringBeforeLast should return the expected result (string)")] + [Theory(DisplayName = "String.SubstringBeforeLast should return the string before the last string delimiter")] [InlineData("First:Second:Third", "First:Second", ":")] [InlineData("12345+678910+12345", "12345+678910", "+")] - public void SubstringBeforeLastShouldReturnTheExpectedResultString(string value, string expected, string delimiter) + public void SubstringBeforeLastShouldProduceExpectedResultString(string value, string expected, string delimiter) { - // Arrange / Act + // When string actual = value.SubstringBeforeLast(delimiter); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "SubstringAfter should return the expected result (char)")] + [Theory(DisplayName = "String.SubstringAfterFirst should return the string after the first char delimiter")] [InlineData("First:Second", "Second", ':')] [InlineData("12345+678910", "678910", '+')] - public void SubstringAfterShouldReturnTheExpectedResultChar(string value, string expected, char delimiter) + public void SubstringAfterFirstShouldProduceExpectedResultChar(string value, string expected, char delimiter) { - // Arrange / Act - string actual = value.SubstringAfter(delimiter); + // When + string actual = value.SubstringAfterFirst(delimiter); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "SubstringAfter should return the expected result (string)")] + [Theory(DisplayName = "String.SubstringAfterFirst should return the string after the first string delimiter")] [InlineData("First:Second", "Second", ":")] [InlineData("12345+678910", "678910", "+")] - public void SubstringAfterShouldReturnTheExpectedResultString(string value, string expected, string delimiter) + public void SubstringAfterFirstShouldProduceExpectedResultString(string value, string expected, string delimiter) { - // Arrange / Act - string actual = value.SubstringAfter(delimiter); + // When + string actual = value.SubstringAfterFirst(delimiter); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "SubstringAfterLast should return the expected result (char)")] + [Theory(DisplayName = "String.SubstringAfterLast should return the string after the last char delimiter")] [InlineData("First:Second:Third", "Third", ':')] [InlineData("12345+678910+12345", "12345", '+')] - public void SubstringAfterLastShouldReturnTheExpectedResultChar(string value, string expected, char delimiter) + public void SubstringAfterLastShouldProduceExpectedResultChar(string value, string expected, char delimiter) { - // Arrange / Act + // When string actual = value.SubstringAfterLast(delimiter); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "SubstringAfterLast should return the expected result (string)")] + [Theory(DisplayName = "String.SubstringAfterLast should return the the string after the last string delimiter")] [InlineData("First:Second:Third", "Third", ":")] [InlineData("12345+678910+12345", "12345", "+")] - public void SubstringAfterLastShouldReturnTheExpectedResultString(string value, string expected, string delimiter) + public void SubstringAfterLastShouldProduceExpectedResultString(string value, string expected, string delimiter) { - // Arrange / Act + // When string actual = value.SubstringAfterLast(delimiter); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "ToByteArray should return the expected result")] - [InlineData("Hello, World!", new byte[] {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21})] - public void ToByteArrayShouldReturnTheExpectedResult(string value, byte[] expected) + [Theory(DisplayName = "String.ToByteArray should produce the byte array equivalent of the current string")] + [InlineData("Hello, World!", new byte[] { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21 })] + public void ToByteArrayShouldProduceExpectedResult(string value, byte[] expected) { - // Arrange / Act + // When byte[] actual = value.ToByteArray(); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "Wrap should return the expected result")] + [Theory(DisplayName = "String.ToDateTime should return the DateTime equivalent of the current string")] + [InlineData("0001-01-01T00:00:00Z", 1, 1, 1, 0, 0, 0)] + [InlineData("1970-01-01T00:00:00Z", 1970, 1, 1, 0, 0, 0)] + [InlineData("9999-12-31T23:59:59Z", 9999, 12, 31, 23, 59, 59)] + public void ToDateTimeShouldProduceExpectedResult(string value, int year, int month, int day, int hour, int minute, int second) + { + // When + // AdjustToUniversal is essential, otherwise the tests may pass locally but fail on a build server in another time-zone. + DateTime actual = value.ToDateTime(styles: DateTimeStyles.AdjustToUniversal); + + // Then + Assert.Equal(year, actual.Year); + Assert.Equal(month, actual.Month); + Assert.Equal(day, actual.Day); + Assert.Equal(hour, actual.Hour); + Assert.Equal(minute, actual.Minute); + Assert.Equal(second, actual.Second); + } + + [Theory(DisplayName = "String.ToDateOnly should return the DateOnly equivalent of the current string")] + [InlineData("0001-01-01", 1, 1, 1)] + [InlineData("1970-01-01", 1970, 1, 1)] + [InlineData("9999-12-31", 9999, 12, 31)] + public void ToDateOnlyShouldProduceExpectedResult(string value, int year, int month, int day) + { + // When + DateOnly actual = value.ToDateOnly(); + + // Then + Assert.Equal(year, actual.Year); + Assert.Equal(month, actual.Month); + Assert.Equal(day, actual.Day); + } + + [Theory(DisplayName = "String.ToTimeOnly should return the TimeOnly equivalent of the current string")] + [InlineData("00:00:00", 0, 0, 0)] + [InlineData("01:01:01", 1, 1, 1)] + [InlineData("23:59:59", 23, 59, 59)] + public void ToTimeOnlyShouldProduceExpectedResult(string value, int hour, int minute, int second) + { + // When + TimeOnly actual = value.ToTimeOnly(); + + // Then + Assert.Equal(hour, actual.Hour); + Assert.Equal(minute, actual.Minute); + Assert.Equal(second, actual.Second); + } + + [Theory(DisplayName = "String.Wrap should wrap the current string value between the before and after string values")] [InlineData("<", "value", ">", "")] [InlineData("BEFORE:", "value", ":AFTER", "BEFORE:value:AFTER")] - public void WrapShouldReturnTheExpectedResult(string before, string value, string after, string expected) + public void WrapShouldProduceExpectedResult(string before, string value, string after, string expected) { - // Arrange / Act + // When string actual = value.Wrap(before, after); - // Assert + // Then Assert.Equal(expected, actual); } } diff --git a/OnixLabs.Core.UnitTests/Text/Base16CodecInvariantTests.cs b/OnixLabs.Core.UnitTests/Text/Base16CodecInvariantTests.cs new file mode 100644 index 0000000..1e99222 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base16CodecInvariantTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base16CodecInvariantTests +{ + [Theory(DisplayName = "Base16Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","4142434445464748494a4b4c4d4e4f505152535455565758595a")] + [InlineData("abcdefghijklmnopqrstuvwxyz","6162636465666768696a6b6c6d6e6f707172737475767778797a")] + [InlineData("0123456789","30313233343536373839")] + public void Base16CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base16; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base16FormatProvider.Invariant); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base16Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("4142434445464748494a4b4c4d4e4f505152535455565758595a","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("6162636465666768696a6b6c6d6e6f707172737475767778797a","abcdefghijklmnopqrstuvwxyz")] + [InlineData("30313233343536373839","0123456789")] + public void Base16CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base16; + + // When + byte[] bytes = codec.Decode(value, Base16FormatProvider.Invariant); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base16CodecLowercaseTests.cs b/OnixLabs.Core.UnitTests/Text/Base16CodecLowercaseTests.cs new file mode 100644 index 0000000..a7a26c5 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base16CodecLowercaseTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base16CodecLowercaseTests +{ + [Theory(DisplayName = "Base16Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","4142434445464748494a4b4c4d4e4f505152535455565758595a")] + [InlineData("abcdefghijklmnopqrstuvwxyz","6162636465666768696a6b6c6d6e6f707172737475767778797a")] + [InlineData("0123456789","30313233343536373839")] + public void Base16CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base16; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base16FormatProvider.Lowercase); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base16Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("4142434445464748494a4b4c4d4e4f505152535455565758595a","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("6162636465666768696a6b6c6d6e6f707172737475767778797a","abcdefghijklmnopqrstuvwxyz")] + [InlineData("30313233343536373839","0123456789")] + public void Base16CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base16; + + // When + byte[] bytes = codec.Decode(value, Base16FormatProvider.Lowercase); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base16CodecUppercaseTests.cs b/OnixLabs.Core.UnitTests/Text/Base16CodecUppercaseTests.cs new file mode 100644 index 0000000..4176c93 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base16CodecUppercaseTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base16CodecUppercaseTests +{ + [Theory(DisplayName = "Base16Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","4142434445464748494A4B4C4D4E4F505152535455565758595A")] + [InlineData("abcdefghijklmnopqrstuvwxyz","6162636465666768696A6B6C6D6E6F707172737475767778797A")] + [InlineData("0123456789","30313233343536373839")] + public void Base16CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base16; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base16FormatProvider.Uppercase); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base16Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("4142434445464748494A4B4C4D4E4F505152535455565758595A","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("6162636465666768696A6B6C6D6E6F707172737475767778797A","abcdefghijklmnopqrstuvwxyz")] + [InlineData("30313233343536373839","0123456789")] + public void Base16CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base16; + + // When + byte[] bytes = codec.Decode(value, Base16FormatProvider.Uppercase); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base16Tests.cs b/OnixLabs.Core.UnitTests/Text/Base16Tests.cs index 2d5cc56..b7216ef 100644 --- a/OnixLabs.Core.UnitTests/Text/Base16Tests.cs +++ b/OnixLabs.Core.UnitTests/Text/Base16Tests.cs @@ -1,17 +1,18 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System.Text; using OnixLabs.Core.Text; using Xunit; @@ -19,50 +20,100 @@ namespace OnixLabs.Core.UnitTests.Text; public sealed class Base16Tests { - [Fact(DisplayName = "Identical Base16 values produce identical hash codes.")] - public void IdenticalBase16ValuesProduceIdenticalHashCodes() + [Fact(DisplayName = "Base16 should not change when modifying the original byte array")] + public void Base16ShouldNotChangeWhenModifyingOriginalByteArray() { - // Arrange - Base16 a = Base16.FromString("abcdefghijklmnopqrstuvwxyz"); - Base16 b = Base16.FromString("abcdefghijklmnopqrstuvwxyz"); + // Given + byte[] bytes = "ABCabc123".ToByteArray(); + Base16 candidate = new(bytes); + const string expected = "414243616263313233"; - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); + // When + bytes[0] = 0; + string actual = candidate.ToString(); - // Assert - Assert.Equal(hashCodeA, hashCodeB); + // Then + Assert.Equal(expected, actual); } - [Theory(DisplayName = "Base16_FromString should produce the expected Base-16 value.")] - [InlineData("31323334353637383930", "1234567890")] - [InlineData("4142434445464748494a4b4c4d4e4f505152535455565758595a", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("6162636465666768696a6b6c6d6e6f707172737475767778797a", "abcdefghijklmnopqrstuvwxyz")] - public void Base16FromStringShouldProduceTheExpectedBase16Value(string expected, string value) + [Fact(DisplayName = "Base16 should not change when modifying the obtained byte array")] + public void Base16ShouldNotChangeWhenModifyingObtainedByteArray() { - // Arrange - Base16 candidate = Base16.FromString(value); + // Given + Base16 candidate = new("ABCabc123".ToByteArray()); + const string expected = "414243616263313233"; - // Act + // When + byte[] bytes = candidate.ToByteArray(); + bytes[0] = 0; string actual = candidate.ToString(); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "Base16_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "31323334353637383930")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "4142434445464748494a4b4c4d4e4f505152535455565758595a")] - [InlineData("abcdefghijklmnopqrstuvwxyz", "6162636465666768696a6b6c6d6e6f707172737475767778797a")] - public void Base16ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) + [Fact(DisplayName = "Base16 values should be identical")] + public void Base16ValuesShouldBeIdentical() { - // Arrange + // Given + Base16 a = new([0, 255]); + Base16 b = new([0, 255]); + + // Then + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + Assert.True(a.Equals(b)); + Assert.True(a == b); + Assert.False(a != b); + } + + [Fact(DisplayName = "Base16 values should not be identical")] + public void Base16ValuesShouldNotBeIdentical() + { + // Given + Base16 a = new([0, 255]); + Base16 b = new([1, 127]); + + // Then + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + Assert.False(a.Equals(b)); + Assert.False(a == b); + Assert.True(a != b); + } + + [Theory(DisplayName = "Base16.Parse should produce the expected result")] + [InlineData("", "")] + [InlineData("4142434445464748494a4b4c4d4e4f505152535455565758595a","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("6162636465666768696a6b6c6d6e6f707172737475767778797a","abcdefghijklmnopqrstuvwxyz")] + [InlineData("30313233343536373839","0123456789")] + public void Base16ParseShouldProduceExpectedResult(string value, string expected) + { + // Given Base16 candidate = Base16.Parse(value); - // Act - string actual = candidate.ToPlainTextString(); + // When + string actual = Encoding.UTF8.GetString(candidate.ToByteArray()); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base16.ToString should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","4142434445464748494a4b4c4d4e4f505152535455565758595a")] + [InlineData("abcdefghijklmnopqrstuvwxyz","6162636465666768696a6b6c6d6e6f707172737475767778797a")] + [InlineData("0123456789","30313233343536373839")] + public void Base16ToStringShouldProduceExpectedResult(string value, string expected) + { + // Given + byte[] bytes = Encoding.UTF8.GetBytes(value); + Base16 candidate = new(bytes); + + // When + string actual = candidate.ToString(); - // Assert + // Then Assert.Equal(expected, actual); } } diff --git a/OnixLabs.Core.UnitTests/Text/Base32Base32HexAlphabetTests.cs b/OnixLabs.Core.UnitTests/Text/Base32Base32HexAlphabetTests.cs deleted file mode 100644 index 0754051..0000000 --- a/OnixLabs.Core.UnitTests/Text/Base32Base32HexAlphabetTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; -using Xunit; - -namespace OnixLabs.Core.UnitTests.Text; - -public sealed class Base32Base32HexAlphabetTests -{ - [Fact(DisplayName = "Identical Base32 values produce identical hash codes.")] - public void IdenticalBase32ValuesProduceIdenticalHashCodes() - { - // Arrange - Base32 a = Base32.FromString("abcdefghijklmnopqrstuvwxyz", Base32Alphabet.Base32Hex); - Base32 b = Base32.FromString("abcdefghijklmnopqrstuvwxyz", Base32Alphabet.Base32Hex); - - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); - - // Assert - Assert.Equal(hashCodeA, hashCodeB); - } - - [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")] - [InlineData("64P36D1L6ORJGE9G", "1234567890")] - [InlineData("85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8======", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8======", "abcdefghijklmnopqrstuvwxyz")] - public void Base32FromStringWithPaddingShouldProduceTheExpectedBase32Value(string expected, string value) - { - // Arrange - Base32 candidate = Base32.FromString(value, Base32Alphabet.Base32Hex, true); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")] - [InlineData("64P36D1L6ORJGE9G", "1234567890")] - [InlineData("85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8", "abcdefghijklmnopqrstuvwxyz")] - public void Base32FromStringWithoutPaddingShouldProduceTheExpectedBase32Value(string expected, string value) - { - // Arrange - Base32 candidate = Base32.FromString(value, Base32Alphabet.Base32Hex, false); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base32_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "64P36D1L6ORJGE9G")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8")] - [InlineData("abcdefghijklmnopqrstuvwxyz", "C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8")] - public void Base32ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) - { - // Arrange - Base32 candidate = Base32.Parse(value, Base32Alphabet.Base32Hex); - - // Act - string actual = candidate.ToPlainTextString(); - - // Assert - Assert.Equal(expected, actual); - } -} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecBase32HexTests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecBase32HexTests.cs new file mode 100644 index 0000000..a5abccb --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecBase32HexTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecBase32HexTests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8")] + [InlineData("abcdefghijklmnopqrstuvwxyz","C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8")] + [InlineData("0123456789","60OJ4CPK6KR3EE1P")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.Base32Hex); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8","abcdefghijklmnopqrstuvwxyz")] + [InlineData("60OJ4CPK6KR3EE1P","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.Base32Hex); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecCrockfordTests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecCrockfordTests.cs new file mode 100644 index 0000000..b75133f --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecCrockfordTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecCrockfordTests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8")] + [InlineData("abcdefghijklmnopqrstuvwxyz","C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8")] + [InlineData("0123456789","60RK4CSM6MV3EE1S")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.Crockford); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8","abcdefghijklmnopqrstuvwxyz")] + [InlineData("60RK4CSM6MV3EE1S","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.Crockford); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecGeoHashTests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecGeoHashTests.cs new file mode 100644 index 0000000..1aa3b0e --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecGeoHashTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecGeoHashTests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","85146j258t3nhkbb9e64umkgb18p4nunbpc5fq2tc8")] + [InlineData("abcdefghijklmnopqrstuvwxyz","d5j66t35dtmqhucbeeq6uvmgf1sr4wvnfpv7fy3tg8")] + [InlineData("0123456789","60sm4dtn6nv3ff1t")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.GeoHash); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("85146j258t3nhkbb9e64umkgb18p4nunbpc5fq2tc8","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("d5j66t35dtmqhucbeeq6uvmgf1sr4wvnfpv7fy3tg8","abcdefghijklmnopqrstuvwxyz")] + [InlineData("60sm4dtn6nv3ff1t","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.GeoHash); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedBase32HexTests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedBase32HexTests.cs new file mode 100644 index 0000000..d1b68db --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedBase32HexTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecPaddedBase32HexTests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8======")] + [InlineData("abcdefghijklmnopqrstuvwxyz","C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8======")] + [InlineData("0123456789","60OJ4CPK6KR3EE1P")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.PaddedBase32Hex); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8======","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8======","abcdefghijklmnopqrstuvwxyz")] + [InlineData("60OJ4CPK6KR3EE1P","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.PaddedBase32Hex); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedCrockfordTests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedCrockfordTests.cs new file mode 100644 index 0000000..ba48e0f --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedCrockfordTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecPaddedCrockfordTests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8======")] + [InlineData("abcdefghijklmnopqrstuvwxyz","C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8======")] + [InlineData("0123456789","60RK4CSM6MV3EE1S")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.PaddedCrockford); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8======","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8======","abcdefghijklmnopqrstuvwxyz")] + [InlineData("60RK4CSM6MV3EE1S","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.PaddedCrockford); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedGeoHashTests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedGeoHashTests.cs new file mode 100644 index 0000000..07c820b --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedGeoHashTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecPaddedGeoHashTests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","85146j258t3nhkbb9e64umkgb18p4nunbpc5fq2tc8======")] + [InlineData("abcdefghijklmnopqrstuvwxyz","d5j66t35dtmqhucbeeq6uvmgf1sr4wvnfpv7fy3tg8======")] + [InlineData("0123456789","60sm4dtn6nv3ff1t")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.PaddedGeoHash); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("85146j258t3nhkbb9e64umkgb18p4nunbpc5fq2tc8======","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("d5j66t35dtmqhucbeeq6uvmgf1sr4wvnfpv7fy3tg8======","abcdefghijklmnopqrstuvwxyz")] + [InlineData("60sm4dtn6nv3ff1t","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.PaddedGeoHash); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedRfc4648Tests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedRfc4648Tests.cs new file mode 100644 index 0000000..da0ba10 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedRfc4648Tests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecPaddedRfc4648Tests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI======")] + [InlineData("abcdefghijklmnopqrstuvwxyz","MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI======")] + [InlineData("0123456789","GAYTEMZUGU3DOOBZ")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.PaddedRfc4648); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI======","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI======","abcdefghijklmnopqrstuvwxyz")] + [InlineData("GAYTEMZUGU3DOOBZ","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.PaddedRfc4648); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedZBase32Tests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedZBase32Tests.cs new file mode 100644 index 0000000..80838c9 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecPaddedZBase32Tests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecPaddedZBase32Tests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me======")] + [InlineData("abcdefghijklmnopqrstuvwxyz","cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe======")] + [InlineData("0123456789","gyaurc3wgw5dqqb3")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.PaddedZBase32); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me======","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe======","abcdefghijklmnopqrstuvwxyz")] + [InlineData("gyaurc3wgw5dqqb3","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.PaddedZBase32); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecRfc4648Tests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecRfc4648Tests.cs new file mode 100644 index 0000000..6a15981 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecRfc4648Tests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecRfc4648Tests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI")] + [InlineData("abcdefghijklmnopqrstuvwxyz","MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI")] + [InlineData("0123456789","GAYTEMZUGU3DOOBZ")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI","abcdefghijklmnopqrstuvwxyz")] + [InlineData("GAYTEMZUGU3DOOBZ","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CodecZBase32Tests.cs b/OnixLabs.Core.UnitTests/Text/Base32CodecZBase32Tests.cs new file mode 100644 index 0000000..23d1832 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32CodecZBase32Tests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32CodecZBase32Tests +{ + [Theory(DisplayName = "Base32Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me")] + [InlineData("abcdefghijklmnopqrstuvwxyz","cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe")] + [InlineData("0123456789","gyaurc3wgw5dqqb3")] + public void Base32CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base32FormatProvider.ZBase32); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe","abcdefghijklmnopqrstuvwxyz")] + [InlineData("gyaurc3wgw5dqqb3","0123456789")] + public void Base32CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base32; + + // When + byte[] bytes = codec.Decode(value, Base32FormatProvider.ZBase32); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32CrockfordAlphabetTests.cs b/OnixLabs.Core.UnitTests/Text/Base32CrockfordAlphabetTests.cs deleted file mode 100644 index ad3c782..0000000 --- a/OnixLabs.Core.UnitTests/Text/Base32CrockfordAlphabetTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; -using Xunit; - -namespace OnixLabs.Core.UnitTests.Text; - -public sealed class Base32CrockfordAlphabetTests -{ - [Fact(DisplayName = "Identical Base32 values produce identical hash codes.")] - public void IdenticalBase32ValuesProduceIdenticalHashCodes() - { - // Arrange - Base32 a = Base32.FromString("abcdefghijklmnopqrstuvwxyz", Base32Alphabet.Crockford); - Base32 b = Base32.FromString("abcdefghijklmnopqrstuvwxyz", Base32Alphabet.Crockford); - - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); - - // Assert - Assert.Equal(hashCodeA, hashCodeB); - } - - [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")] - [InlineData("64S36D1N6RVKGE9G", "1234567890")] - [InlineData("85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8======", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8======", "abcdefghijklmnopqrstuvwxyz")] - public void Base32FromStringWithPaddingShouldProduceTheExpectedBase32Value(string expected, string value) - { - // Arrange - Base32 candidate = Base32.FromString(value, Base32Alphabet.Crockford, true); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")] - [InlineData("64S36D1N6RVKGE9G", "1234567890")] - [InlineData("85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8", "abcdefghijklmnopqrstuvwxyz")] - public void Base32FromStringWithoutPaddingShouldProduceTheExpectedBase32Value(string expected, string value) - { - // Arrange - Base32 candidate = Base32.FromString(value, Base32Alphabet.Crockford, false); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base32_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "64S36D1N6RVKGE9G")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8")] - [InlineData("abcdefghijklmnopqrstuvwxyz", "C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8")] - public void Base32ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) - { - // Arrange - Base32 candidate = Base32.Parse(value, Base32Alphabet.Crockford); - - // Act - string actual = candidate.ToPlainTextString(); - - // Assert - Assert.Equal(expected, actual); - } -} diff --git a/OnixLabs.Core.UnitTests/Text/Base32DefaultAlphabetTests.cs b/OnixLabs.Core.UnitTests/Text/Base32DefaultAlphabetTests.cs deleted file mode 100644 index 64be871..0000000 --- a/OnixLabs.Core.UnitTests/Text/Base32DefaultAlphabetTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; -using Xunit; - -namespace OnixLabs.Core.UnitTests.Text; - -public sealed class Base32DefaultAlphabetTests -{ - [Fact(DisplayName = "Identical Base32 values produce identical hash codes.")] - public void IdenticalBase32ValuesProduceIdenticalHashCodes() - { - // Arrange - Base32 a = Base32.FromString("abcdefghijklmnopqrstuvwxyz", Base32Alphabet.Default); - Base32 b = Base32.FromString("abcdefghijklmnopqrstuvwxyz", Base32Alphabet.Default); - - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); - - // Assert - Assert.Equal(hashCodeA, hashCodeB); - } - - [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")] - [InlineData("GEZDGNBVGY3TQOJQ", "1234567890")] - [InlineData("IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI======", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI======", "abcdefghijklmnopqrstuvwxyz")] - public void Base32FromStringWithPaddingShouldProduceTheExpectedBase32Value(string expected, string value) - { - // Arrange - Base32 candidate = Base32.FromString(value, Base32Alphabet.Default, true); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")] - [InlineData("GEZDGNBVGY3TQOJQ", "1234567890")] - [InlineData("IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI", "abcdefghijklmnopqrstuvwxyz")] - public void Base32FromStringWithoutPaddingShouldProduceTheExpectedBase32Value(string expected, string value) - { - // Arrange - Base32 candidate = Base32.FromString(value, Base32Alphabet.Default, false); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base32_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "GEZDGNBVGY3TQOJQ")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI")] - [InlineData("abcdefghijklmnopqrstuvwxyz", "MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI")] - public void Base32ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) - { - // Arrange - Base32 candidate = Base32.Parse(value, Base32Alphabet.Default); - - // Act - string actual = candidate.ToPlainTextString(); - - // Assert - Assert.Equal(expected, actual); - } -} diff --git a/OnixLabs.Core.UnitTests/Text/Base32Tests.cs b/OnixLabs.Core.UnitTests/Text/Base32Tests.cs new file mode 100644 index 0000000..5ff7d84 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base32Tests.cs @@ -0,0 +1,119 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base32Tests +{ + [Fact(DisplayName = "Base32 should not change when modifying the original byte array")] + public void Base32ShouldNotChangeWhenModifyingOriginalByteArray() + { + // Given + byte[] bytes = "ABCabc123".ToByteArray(); + Base32 candidate = new(bytes); + const string expected = "IFBEGYLCMMYTEMY"; + + // When + bytes[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Base32 should not change when modifying the obtained byte array")] + public void Base32ShouldNotChangeWhenModifyingObtainedByteArray() + { + // Given + Base32 candidate = new("ABCabc123".ToByteArray()); + const string expected = "IFBEGYLCMMYTEMY"; + + // When + byte[] bytes = candidate.ToByteArray(); + bytes[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Base32 values should be identical")] + public void Base32ValuesShouldBeIdentical() + { + // Given + Base32 a = new([0, 255]); + Base32 b = new([0, 255]); + + // Then + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + Assert.True(a.Equals(b)); + Assert.True(a == b); + Assert.False(a != b); + } + + [Fact(DisplayName = "Base32 values should not be identical")] + public void Base32ValuesShouldNotBeIdentical() + { + // Given + Base32 a = new([0, 255]); + Base32 b = new([1, 127]); + + // Then + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + Assert.False(a.Equals(b)); + Assert.False(a == b); + Assert.True(a != b); + } + + [Theory(DisplayName = "Base32.Parse should produce the expected result")] + [InlineData("", "")] + [InlineData("IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI","abcdefghijklmnopqrstuvwxyz")] + [InlineData("GAYTEMZUGU3DOOBZ","0123456789")] + public void Base32ParseShouldProduceExpectedResult(string value, string expected) + { + // Given + Base32 candidate = Base32.Parse(value); + + // When + string actual = Encoding.UTF8.GetString(candidate.ToByteArray()); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base32.ToString should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI")] + [InlineData("abcdefghijklmnopqrstuvwxyz","MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI")] + [InlineData("0123456789","GAYTEMZUGU3DOOBZ")] + public void Base32ToStringShouldProduceExpectedResult(string value, string expected) + { + // Given + byte[] bytes = Encoding.UTF8.GetBytes(value); + Base32 candidate = new(bytes); + + // When + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base32ZBase32AlphabetTests.cs b/OnixLabs.Core.UnitTests/Text/Base32ZBase32AlphabetTests.cs deleted file mode 100644 index 9fcf4a5..0000000 --- a/OnixLabs.Core.UnitTests/Text/Base32ZBase32AlphabetTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; -using Xunit; - -namespace OnixLabs.Core.UnitTests.Text; - -public sealed class Base32ZBase32AlphabetTests -{ - [Fact(DisplayName = "Identical Base32 values produce identical hash codes.")] - public void IdenticalBase32ValuesProduceIdenticalHashCodes() - { - // Arrange - Base32 a = Base32.FromString("abcdefghijklmnopqrstuvwxyz", Base32Alphabet.ZBase32); - Base32 b = Base32.FromString("abcdefghijklmnopqrstuvwxyz", Base32Alphabet.ZBase32); - - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); - - // Assert - Assert.Equal(hashCodeA, hashCodeB); - } - - [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")] - [InlineData("gr3dgpbiga5uoqjo", "1234567890")] - [InlineData("efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me======", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe======", "abcdefghijklmnopqrstuvwxyz")] - public void Base32FromStringWithPaddingShouldProduceTheExpectedBase32Value(string expected, string value) - { - // Arrange - Base32 candidate = Base32.FromString(value, Base32Alphabet.ZBase32, true); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")] - [InlineData("gr3dgpbiga5uoqjo", "1234567890")] - [InlineData("efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe", "abcdefghijklmnopqrstuvwxyz")] - public void Base32FromStringWithoutPaddingShouldProduceTheExpectedBase32Value(string expected, string value) - { - // Arrange - Base32 candidate = Base32.FromString(value, Base32Alphabet.ZBase32, false); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base32_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "gr3dgpbiga5uoqjo")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me")] - [InlineData("abcdefghijklmnopqrstuvwxyz", "cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe")] - public void Base32ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) - { - // Arrange - Base32 candidate = Base32.Parse(value, Base32Alphabet.ZBase32); - - // Act - string actual = candidate.ToPlainTextString(); - - // Assert - Assert.Equal(expected, actual); - } -} diff --git a/OnixLabs.Core.UnitTests/Text/Base58CodecBitcoinTests.cs b/OnixLabs.Core.UnitTests/Text/Base58CodecBitcoinTests.cs new file mode 100644 index 0000000..b0b0d1c --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base58CodecBitcoinTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base58CodecBitcoinTests +{ + [Theory(DisplayName = "Base58Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD")] + [InlineData("abcdefghijklmnopqrstuvwxyz","3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f")] + [InlineData("0123456789","3i37NcgooY8f1S")] + public void Base58CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base58; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base58FormatProvider.Bitcoin); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base58Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f","abcdefghijklmnopqrstuvwxyz")] + [InlineData("3i37NcgooY8f1S","0123456789")] + public void Base58CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base58; + + // When + byte[] bytes = codec.Decode(value, Base58FormatProvider.Bitcoin); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base58CodecFlickrTests.cs b/OnixLabs.Core.UnitTests/Text/Base58CodecFlickrTests.cs new file mode 100644 index 0000000..9e148fd --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base58CodecFlickrTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base58CodecFlickrTests +{ + [Theory(DisplayName = "Base58Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","2ZUfwsirsqj6erKTQGm2pdbKcMh1t46cMXzd")] + [InlineData("abcdefghijklmnopqrstuvwxyz","3YXt3U1HFx8vKFTJj92EAipcC4byHHs1V25E")] + [InlineData("0123456789","3H37nBFNNx8E1r")] + public void Base58CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base58; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base58FormatProvider.Flickr); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base58Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("2ZUfwsirsqj6erKTQGm2pdbKcMh1t46cMXzd","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("3YXt3U1HFx8vKFTJj92EAipcC4byHHs1V25E","abcdefghijklmnopqrstuvwxyz")] + [InlineData("3H37nBFNNx8E1r","0123456789")] + public void Base58CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base58; + + // When + byte[] bytes = codec.Decode(value, Base58FormatProvider.Flickr); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base58CodecRippleTests.cs b/OnixLabs.Core.UnitTests/Text/Base58CodecRippleTests.cs new file mode 100644 index 0000000..562835d --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base58CodecRippleTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base58CodecRippleTests +{ + [Theory(DisplayName = "Base58Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "pzuEXTJSTRKaNSktq6MpQDBkU8Hr7haU8x2D")] + [InlineData("abcdefghijklmnopqrstuvwxyz", "syx7sur5gY3WkgtjK9pCbJQUdhBZ55TrvpnC")] + [InlineData("0123456789", "s5sf4cgooY3CrS")] + public void Base58CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base58; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes, Base58FormatProvider.Ripple); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base58Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("pzuEXTJSTRKaNSktq6MpQDBkU8Hr7haU8x2D", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("syx7sur5gY3WkgtjK9pCbJQUdhBZ55TrvpnC", "abcdefghijklmnopqrstuvwxyz")] + [InlineData("s5sf4cgooY3CrS", "0123456789")] + public void Base58CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base58; + + // When + byte[] bytes = codec.Decode(value, Base58FormatProvider.Ripple); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base58DefaultAlphabetTests.cs b/OnixLabs.Core.UnitTests/Text/Base58DefaultAlphabetTests.cs deleted file mode 100644 index f59cb61..0000000 --- a/OnixLabs.Core.UnitTests/Text/Base58DefaultAlphabetTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; -using Xunit; - -namespace OnixLabs.Core.UnitTests.Text; - -public sealed class Base58DefaultAlphabetTests -{ - [Fact(DisplayName = "Identical Base58 values produce identical hash codes.")] - public void IdenticalBase58ValuesProduceIdenticalHashCodes() - { - // Arrange - Base58 a = Base58.FromString("abcdefghijklmnopqrstuvwxyz", Base58Alphabet.Default); - Base58 b = Base58.FromString("abcdefghijklmnopqrstuvwxyz", Base58Alphabet.Default); - - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); - - // Assert - Assert.Equal(hashCodeA, hashCodeB); - } - - [Theory(DisplayName = "Base58_FromString should produce the expected Base-58 value.")] - [InlineData("3mJr7AoUCHxNqd", "1234567890")] - [InlineData("2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f", "abcdefghijklmnopqrstuvwxyz")] - public void Base58FromStringShouldProduceTheExpectedBase58Value(string expected, string value) - { - // Arrange - Base58 candidate = Base58.FromString(value, Base58Alphabet.Default); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base58_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "3mJr7AoUCHxNqd")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD")] - [InlineData("abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f")] - public void Base58ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) - { - // Arrange - Base58 candidate = Base58.Parse(value, Base58Alphabet.Default); - - // Act - string actual = candidate.ToPlainTextString(); - - // Assert - Assert.Equal(expected, actual); - } -} diff --git a/OnixLabs.Core.UnitTests/Text/Base58FlickrAlphabetTests.cs b/OnixLabs.Core.UnitTests/Text/Base58FlickrAlphabetTests.cs deleted file mode 100644 index 64cc7e2..0000000 --- a/OnixLabs.Core.UnitTests/Text/Base58FlickrAlphabetTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; -using Xunit; - -namespace OnixLabs.Core.UnitTests.Text; - -public sealed class Base58FlickrAlphabetTests -{ - [Fact(DisplayName = "Identical Base58 values produce identical hash codes.")] - public void IdenticalBase58ValuesProduceIdenticalHashCodes() - { - // Arrange - Base58 a = Base58.FromString("abcdefghijklmnopqrstuvwxyz", Base58Alphabet.Flickr); - Base58 b = Base58.FromString("abcdefghijklmnopqrstuvwxyz", Base58Alphabet.Flickr); - - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); - - // Assert - Assert.Equal(hashCodeA, hashCodeB); - } - - [Theory(DisplayName = "Base58_FromString should produce the expected Base-58 value.")] - [InlineData("3LiR7aNtchXnQC", "1234567890")] - [InlineData("2ZUfwsirsqj6erKTQGm2pdbKcMh1t46cMXzd", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("3YXt3U1HFx8vKFTJj92EAipcC4byHHs1V25E", "abcdefghijklmnopqrstuvwxyz")] - public void Base58FromStringShouldProduceTheExpectedBase58Value(string expected, string value) - { - // Arrange - Base58 candidate = Base58.FromString(value, Base58Alphabet.Flickr); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base58_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "3LiR7aNtchXnQC")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "2ZUfwsirsqj6erKTQGm2pdbKcMh1t46cMXzd")] - [InlineData("abcdefghijklmnopqrstuvwxyz", "3YXt3U1HFx8vKFTJj92EAipcC4byHHs1V25E")] - public void Base58ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) - { - // Arrange - Base58 candidate = Base58.Parse(value, Base58Alphabet.Flickr); - - // Act - string actual = candidate.ToPlainTextString(); - - // Assert - Assert.Equal(expected, actual); - } -} diff --git a/OnixLabs.Core.UnitTests/Text/Base58RippleAlphabetTests.cs b/OnixLabs.Core.UnitTests/Text/Base58RippleAlphabetTests.cs deleted file mode 100644 index 381776c..0000000 --- a/OnixLabs.Core.UnitTests/Text/Base58RippleAlphabetTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; -using Xunit; - -namespace OnixLabs.Core.UnitTests.Text; - -public sealed class Base58RippleAlphabetTests -{ - [Fact(DisplayName = "Identical Base58 values produce identical hash codes.")] - public void IdenticalBase58ValuesProduceIdenticalHashCodes() - { - // Arrange - Base58 a = Base58.FromString("abcdefghijklmnopqrstuvwxyz", Base58Alphabet.Ripple); - Base58 b = Base58.FromString("abcdefghijklmnopqrstuvwxyz", Base58Alphabet.Ripple); - - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); - - // Assert - Assert.Equal(hashCodeA, hashCodeB); - } - - [Theory(DisplayName = "Base58_FromString should produce the expected Base-58 value.")] - [InlineData("smJifwo7UHx4qd", "1234567890")] - [InlineData("pzuEXTJSTRKaNSktq6MpQDBkU8Hr7haU8x2D", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("syx7sur5gY3WkgtjK9pCbJQUdhBZ55TrvpnC", "abcdefghijklmnopqrstuvwxyz")] - public void Base58FromStringShouldProduceTheExpectedBase58Value(string expected, string value) - { - // Arrange - Base58 candidate = Base58.FromString(value, Base58Alphabet.Ripple); - - // Act - string actual = candidate.ToString(); - - // Assert - Assert.Equal(expected, actual); - } - - [Theory(DisplayName = "Base58_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "smJifwo7UHx4qd")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "pzuEXTJSTRKaNSktq6MpQDBkU8Hr7haU8x2D")] - [InlineData("abcdefghijklmnopqrstuvwxyz", "syx7sur5gY3WkgtjK9pCbJQUdhBZ55TrvpnC")] - public void Base58ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) - { - // Arrange - Base58 candidate = Base58.Parse(value, Base58Alphabet.Ripple); - - // Act - string actual = candidate.ToPlainTextString(); - - // Assert - Assert.Equal(expected, actual); - } -} diff --git a/OnixLabs.Core.UnitTests/Text/Base58Tests.cs b/OnixLabs.Core.UnitTests/Text/Base58Tests.cs new file mode 100644 index 0000000..de00880 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base58Tests.cs @@ -0,0 +1,119 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base58Tests +{ + [Fact(DisplayName = "Base58 should not change when modifying the original byte array")] + public void Base58ShouldNotChangeWhenModifyingOriginalByteArray() + { + // Given + byte[] bytes = "ABCabc123".ToByteArray(); + Base58 candidate = new(bytes); + const string expected = "qBLgTCSW82Hg"; + + // When + bytes[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Base58 should not change when modifying the obtained byte array")] + public void Base58ShouldNotChangeWhenModifyingObtainedByteArray() + { + // Given + Base58 candidate = new("ABCabc123".ToByteArray()); + const string expected = "qBLgTCSW82Hg"; + + // When + byte[] bytes = candidate.ToByteArray(); + bytes[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Base58 values should be identical")] + public void Base58ValuesShouldBeIdentical() + { + // Given + Base58 a = new([0, 255]); + Base58 b = new([0, 255]); + + // Then + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + Assert.True(a.Equals(b)); + Assert.True(a == b); + Assert.False(a != b); + } + + [Fact(DisplayName = "Base58 values should not be identical")] + public void Base58ValuesShouldNotBeIdentical() + { + // Given + Base58 a = new([0, 255]); + Base58 b = new([1, 127]); + + // Then + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + Assert.False(a.Equals(b)); + Assert.False(a == b); + Assert.True(a != b); + } + + [Theory(DisplayName = "Base58.Parse should produce the expected result")] + [InlineData("", "")] + [InlineData("2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD","ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f","abcdefghijklmnopqrstuvwxyz")] + [InlineData("3i37NcgooY8f1S","0123456789")] + public void Base58ParseShouldProduceExpectedResult(string value, string expected) + { + // Given + Base58 candidate = Base58.Parse(value); + + // When + string actual = Encoding.UTF8.GetString(candidate.ToByteArray()); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base58.ToString should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ","2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD")] + [InlineData("abcdefghijklmnopqrstuvwxyz","3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f")] + [InlineData("0123456789","3i37NcgooY8f1S")] + public void Base58ToStringShouldProduceExpectedResult(string value, string expected) + { + // Given + byte[] bytes = Encoding.UTF8.GetBytes(value); + Base58 candidate = new(bytes); + + // When + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base64CodecTests.cs b/OnixLabs.Core.UnitTests/Text/Base64CodecTests.cs new file mode 100644 index 0000000..fea69f2 --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/Base64CodecTests.cs @@ -0,0 +1,58 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class Base64CodecTests +{ + [Theory(DisplayName = "Base64Codec.Encode should produce the expected result")] + [InlineData("", "")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=")] + [InlineData("abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=")] + [InlineData("0123456789", "MDEyMzQ1Njc4OQ==")] + public void Base64CodecEncodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base64; + byte[] bytes = value.ToByteArray(); + + // When + string actual = codec.Encode(bytes); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Base64Codec.Decode should produce the expected result")] + [InlineData("", "")] + [InlineData("QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", "abcdefghijklmnopqrstuvwxyz")] + [InlineData("MDEyMzQ1Njc4OQ==", "0123456789")] + public void Base64CodecDecodeShouldProduceExpectedResult(string value, string expected) + { + // Given + IBaseCodec codec = IBaseCodec.Base64; + + // When + byte[] bytes = codec.Decode(value); + string actual = Encoding.UTF8.GetString(bytes); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Core.UnitTests/Text/Base64Tests.cs b/OnixLabs.Core.UnitTests/Text/Base64Tests.cs index d9da9b7..7b98b04 100644 --- a/OnixLabs.Core.UnitTests/Text/Base64Tests.cs +++ b/OnixLabs.Core.UnitTests/Text/Base64Tests.cs @@ -1,17 +1,18 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System.Text; using OnixLabs.Core.Text; using Xunit; @@ -19,50 +20,100 @@ namespace OnixLabs.Core.UnitTests.Text; public sealed class Base64Tests { - [Fact(DisplayName = "Identical Base64 values produce identical hash codes.")] - public void IdenticalBase64ValuesProduceIdenticalHashCodes() + [Fact(DisplayName = "Base64 should not change when modifying the original byte array")] + public void Base64ShouldNotChangeWhenModifyingOriginalByteArray() { - // Arrange - Base64 a = Base64.FromString("abcdefghijklmnopqrstuvwxyz"); - Base64 b = Base64.FromString("abcdefghijklmnopqrstuvwxyz"); + // Given + byte[] bytes = "ABCabc123".ToByteArray(); + Base64 candidate = new(bytes); + const string expected = "QUJDYWJjMTIz"; - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); + // When + bytes[0] = 0; + string actual = candidate.ToString(); - // Assert - Assert.Equal(hashCodeA, hashCodeB); + // Then + Assert.Equal(expected, actual); } - [Theory(DisplayName = "Base64_FromString should produce the expected Base-64 value.")] - [InlineData("MTIzNDU2Nzg5MA==", "1234567890")] + [Fact(DisplayName = "Base64 should not change when modifying the obtained byte array")] + public void Base64ShouldNotChangeWhenModifyingObtainedByteArray() + { + // Given + Base64 candidate = new("ABCabc123".ToByteArray()); + const string expected = "QUJDYWJjMTIz"; + + // When + byte[] bytes = candidate.ToByteArray(); + bytes[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Base64 values should be identical")] + public void Base64ValuesShouldBeIdentical() + { + // Given + Base64 a = new([0, 255]); + Base64 b = new([0, 255]); + + // Then + Assert.Equal(a, b); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + Assert.True(a.Equals(b)); + Assert.True(a == b); + Assert.False(a != b); + } + + [Fact(DisplayName = "Base64 values should not be identical")] + public void Base64ValuesShouldNotBeIdentical() + { + // Given + Base64 a = new([0, 255]); + Base64 b = new([1, 127]); + + // Then + Assert.NotEqual(a, b); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + Assert.False(a.Equals(b)); + Assert.False(a == b); + Assert.True(a != b); + } + + [Theory(DisplayName = "Base64.Parse should produce the expected result")] + [InlineData("", "")] [InlineData("QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] [InlineData("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", "abcdefghijklmnopqrstuvwxyz")] - public void Base64FromStringShouldProduceTheExpectedBase64Value(string expected, string value) + [InlineData("MDEyMzQ1Njc4OQ==", "0123456789")] + public void Base64ParseShouldProduceExpectedResult(string value, string expected) { - // Arrange - Base64 candidate = Base64.FromString(value); + // Given + Base64 candidate = Base64.Parse(value); - // Act - string actual = candidate.ToString(); + // When + string actual = Encoding.UTF8.GetString(candidate.ToByteArray()); - // Assert + // Then Assert.Equal(expected, actual); } - [Theory(DisplayName = "Base64_Parse should produce the expected plain text value.")] - [InlineData("1234567890", "MTIzNDU2Nzg5MA==")] + [Theory(DisplayName = "Base64.ToString should produce the expected result")] + [InlineData("", "")] [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=")] [InlineData("abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=")] - public void Base64ParseShouldProduceTheExpectedPlainTextValue(string expected, string value) + [InlineData("0123456789", "MDEyMzQ1Njc4OQ==")] + public void Base64ToStringShouldProduceExpectedResult(string value, string expected) { - // Arrange - Base64 candidate = Base64.Parse(value); + // Given + byte[] bytes = Encoding.UTF8.GetBytes(value); + Base64 candidate = new(bytes); - // Act - string actual = candidate.ToPlainTextString(); + // When + string actual = candidate.ToString(); - // Assert + // Then Assert.Equal(expected, actual); } } diff --git a/OnixLabs.Core.UnitTests/Text/StringBuilderExtensionTests.cs b/OnixLabs.Core.UnitTests/Text/StringBuilderExtensionTests.cs new file mode 100644 index 0000000..868765d --- /dev/null +++ b/OnixLabs.Core.UnitTests/Text/StringBuilderExtensionTests.cs @@ -0,0 +1,152 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text; +using OnixLabs.Core.Text; +using Xunit; + +namespace OnixLabs.Core.UnitTests.Text; + +public sealed class StringBuilderExtensionTests +{ + [Fact(DisplayName = "StringBuilder.Append should produce the expected result")] + public void StringBuilderAppendShouldProduceTheExpectedResult() + { + // Given + StringBuilder builder = new("ABC"); + + // When + builder.Append("XYZ", 123, false); + + // Then + Assert.Equal("ABCXYZ123False", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.Prepend should produce the expected result")] + public void StringBuilderPrependShouldProduceTheExpectedResult() + { + // Given + StringBuilder builder = new("ABC"); + + // When + builder.Prepend("XYZ", 123, false); + + // Then + Assert.Equal("XYZ123FalseABC", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.Trim should produce the expected result (char)")] + public void StringBuilderTrimShouldProduceTheExpectedResultChar() + { + // Given + StringBuilder builder = new("###ABC###"); + + // When + builder.Trim('#'); + + // Then + Assert.Equal("ABC", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.TrimEnd should produce the expected result (char)")] + public void StringBuilderTrimEndShouldProduceTheExpectedResultChar() + { + // Given + StringBuilder builder = new("###ABC###"); + + // When + builder.TrimEnd('#'); + + // Then + Assert.Equal("###ABC", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.TrimStart should produce the expected result (char)")] + public void StringBuilderTrimStartShouldProduceTheExpectedResultChar() + { + // Given + StringBuilder builder = new("###ABC###"); + + // When + builder.TrimStart('#'); + + // Then + Assert.Equal("ABC###", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.Trim should produce the expected result (string)")] + public void StringBuilderTrimShouldProduceTheExpectedResultString() + { + // Given + StringBuilder builder = new("#-#ABC#-#"); + + // When + builder.Trim("#-#"); + + // Then + Assert.Equal("ABC", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.TrimEnd should produce the expected result (string)")] + public void StringBuilderTrimEndShouldProduceTheExpectedResultString() + { + // Given + StringBuilder builder = new("#-#ABC#-#"); + + // When + builder.TrimEnd("#-#"); + + // Then + Assert.Equal("#-#ABC", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.TrimStart should produce the expected result (string)")] + public void StringBuilderTrimStartShouldProduceTheExpectedResultString() + { + // Given + StringBuilder builder = new("#-#ABC#-#"); + + // When + builder.TrimStart("#-#"); + + // Then + Assert.Equal("ABC#-#", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.Wrap should produce the expected result (char)")] + public void StringBuilderWrapShouldProduceTheExpectedResultChar() + { + // Given + StringBuilder builder = new("ABC"); + + // When + builder.Wrap('(', ')'); + + // Then + Assert.Equal("(ABC)", builder.ToString()); + } + + [Fact(DisplayName = "StringBuilder.Wrap should produce the expected result (string)")] + public void StringBuilderWrapShouldProduceTheExpectedResultString() + { + // Given + StringBuilder builder = new("ABC"); + + // When + builder.Wrap("123", "XYZ"); + + // Then + Assert.Equal("123ABCXYZ", builder.ToString()); + } +} diff --git a/OnixLabs.Core/Enumeration.Comparable.cs b/OnixLabs.Core/Enumeration.Comparable.cs index 964c534..8275d76 100644 --- a/OnixLabs.Core/Enumeration.Comparable.cs +++ b/OnixLabs.Core/Enumeration.Comparable.cs @@ -1,33 +1,31 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; - namespace OnixLabs.Core; -public abstract partial class Enumeration : IComparable, IComparable +public abstract partial class Enumeration { /// /// Compares the current instance with another object of the same type and returns an integer that indicates /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the /// other object. /// - /// An object to compare with this instance. + /// An object to compare with the current instance. /// Returns a value that indicates the relative order of the objects being compared. - public int CompareTo(object? obj) + public int CompareTo(T? other) { - return CompareTo(obj as T); + return Value.CompareTo(other?.Value); } /// @@ -35,10 +33,10 @@ public int CompareTo(object? obj) /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the /// other object. /// - /// An object to compare with this instance. + /// An object to compare with the current instance. /// Returns a value that indicates the relative order of the objects being compared. - public int CompareTo(T? other) + public int CompareTo(object? obj) { - return Value.CompareTo(other?.Value); + return this.CompareToObject(obj); } } diff --git a/OnixLabs.Core/Enumeration.Equatable.cs b/OnixLabs.Core/Enumeration.Equatable.cs index 93b2602..0cb68f1 100644 --- a/OnixLabs.Core/Enumeration.Equatable.cs +++ b/OnixLabs.Core/Enumeration.Equatable.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,35 +16,13 @@ namespace OnixLabs.Core; -public abstract partial class Enumeration : IEquatable +public abstract partial class Enumeration { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(Enumeration a, Enumeration b) - { - return Equals(a, b); - } - - /// - /// Performs an inequality check between two object instances. - /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(Enumeration a, Enumeration b) - { - return !Equals(a, b); - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// Returns true if the current object is equal to the other parameter; otherwise, false. + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . public bool Equals(T? other) { return ReferenceEquals(this, other) @@ -55,21 +33,43 @@ public bool Equals(T? other) } /// - /// Checks for equality between this instance and another object. + /// Checks for equality between the current instance and another object. /// /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public sealed override bool Equals(object? obj) + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { return Equals(obj as T); } /// - /// Serves as a hash code function for this instance. + /// Serves as a hash code function for the current instance. /// - /// A hash code for this instance. + /// Returns a hash code for the current instance. public override int GetHashCode() { return HashCode.Combine(GetType(), Name, Value); } + + /// + /// Performs an equality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(Enumeration left, Enumeration right) + { + return Equals(left, right); + } + + /// + /// Performs an inequality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(Enumeration left, Enumeration right) + { + return !Equals(left, right); + } } diff --git a/OnixLabs.Core/Enumeration.From.cs b/OnixLabs.Core/Enumeration.From.cs index ea7c1d9..467e18c 100644 --- a/OnixLabs.Core/Enumeration.From.cs +++ b/OnixLabs.Core/Enumeration.From.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +16,6 @@ using System.Collections.Generic; using System.Linq; using OnixLabs.Core.Linq; -using static OnixLabs.Core.Preconditions; namespace OnixLabs.Core; @@ -32,8 +31,8 @@ public static T FromName(string name) { IEnumerable results = GetAll().Where(entry => entry.Name == name).ToArray(); - Check(results.IsNotEmpty(), $"Enumeration entry for name '{name}' not found in {typeof(T).Name}."); - Check(results.IsSingle(), $"Multiple enumeration entries for name '{name}' found in {typeof(T).Name}."); + Require(results.IsNotEmpty(), $"Enumeration entry for name '{name}' not found in {typeof(T).Name}.", nameof(name)); + Require(results.IsSingle(), $"Multiple enumeration entries for name '{name}' found in {typeof(T).Name}.", nameof(name)); return results.Single(); } @@ -48,8 +47,8 @@ public static T FromValue(int value) { IEnumerable results = GetAll().Where(entry => entry.Value == value).ToArray(); - Check(results.IsNotEmpty(), $"Enumeration entry for value '{value}' not found in {typeof(T).Name}."); - Check(results.IsSingle(), $"Multiple enumeration entries for value '{value}' found in {typeof(T).Name}."); + Require(results.IsNotEmpty(), $"Enumeration entry for value '{value}' not found in {typeof(T).Name}.", nameof(value)); + Require(results.IsSingle(), $"Multiple enumeration entries for value '{value}' found in {typeof(T).Name}.", nameof(value)); return results.Single(); } diff --git a/OnixLabs.Core/Enumeration.Get.cs b/OnixLabs.Core/Enumeration.Get.cs index 7261703..8acee6d 100644 --- a/OnixLabs.Core/Enumeration.Get.cs +++ b/OnixLabs.Core/Enumeration.Get.cs @@ -1,21 +1,21 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Collections.Immutable; +using System.Collections.Frozen; +using System.Collections.Generic; using System.Linq; using System.Reflection; -using OnixLabs.Core.Linq; namespace OnixLabs.Core; @@ -25,39 +25,39 @@ public abstract partial class Enumeration /// Gets all of the enumeration entries for the current type. /// /// Returns all of the enumeration entries for the current type. - public static ImmutableHashSet GetAll() + public static IReadOnlySet GetAll() { return typeof(T) .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) .Select(field => field.GetValue(null)) - .WhereIs() - .ToImmutableHashSet(); + .OfType() + .ToFrozenSet(); } /// /// Gets all of the enumeration entries for the current type. /// /// Returns all of the enumeration entries for the current type. - public static ImmutableHashSet<(int Value, string Name)> GetEntries() + public static IReadOnlySet<(int Value, string Name)> GetEntries() { - return GetAll().Select(entry => entry.ToEntry()).ToImmutableHashSet(); + return GetAll().Select(entry => entry.ToEntry()).ToFrozenSet(); } /// /// Gets all of the enumeration names for the current type. /// /// Returns all of the enumeration names for the current type. - public static ImmutableHashSet GetNames() + public static IReadOnlySet GetNames() { - return GetAll().Select(entry => entry.Name).ToImmutableHashSet(); + return GetAll().Select(entry => entry.Name).ToFrozenSet(); } /// /// Gets all of the enumeration values for the current type. /// /// Returns all of the enumeration values for the current type. - public static ImmutableHashSet GetValues() + public static IReadOnlySet GetValues() { - return GetAll().Select(entry => entry.Value).ToImmutableHashSet(); + return GetAll().Select(entry => entry.Value).ToFrozenSet(); } -} \ No newline at end of file +} diff --git a/OnixLabs.Core/Enumeration.To.cs b/OnixLabs.Core/Enumeration.To.cs index 5432deb..800f49d 100644 --- a/OnixLabs.Core/Enumeration.To.cs +++ b/OnixLabs.Core/Enumeration.To.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,7 +19,7 @@ namespace OnixLabs.Core; public abstract partial class Enumeration { /// - /// Returns a that represents the current object. + /// Returns a that represents the current object. /// /// Returns a tuple that represents the current object. public (int Value, string Name) ToEntry() @@ -28,11 +28,11 @@ public abstract partial class Enumeration } /// - /// Returns a that represents the current object. + /// Returns a that represents the current object. /// - /// A that represents the current object. + /// Returns a that represents the current object. public override string ToString() { return Name; } -} \ No newline at end of file +} diff --git a/OnixLabs.Core/Enumeration.cs b/OnixLabs.Core/Enumeration.cs index cf98ae2..7d9f063 100644 --- a/OnixLabs.Core/Enumeration.cs +++ b/OnixLabs.Core/Enumeration.cs @@ -1,43 +1,36 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Core; /// -/// Represents the base class for implementing enumeration classes. +/// Represents the base class for implementing enumerations. /// +/// The value of the enumeration entry. +/// The name of the enumeration entry. /// The underlying enumeration type. -public abstract partial class Enumeration where T : Enumeration +public abstract partial class Enumeration(int value, string name) : IEquatable, IComparable, IComparable where T : Enumeration { - /// - /// Initializes a new instance of the class. - /// - /// The value of the enumeration entry. - /// The name of the enumeration entry. - protected Enumeration(int value, string name) - { - Value = value; - Name = name; - } - /// /// Gets the name of the enumeration entry. /// - public string Name { get; } + public string Name { get; } = name; /// /// Gets the value of the enumeration entry. /// - public int Value { get; } + public int Value { get; } = value; } diff --git a/OnixLabs.Core/ArrayExtensions.cs b/OnixLabs.Core/Extensions.Array.cs similarity index 55% rename from OnixLabs.Core/ArrayExtensions.cs rename to OnixLabs.Core/Extensions.Array.cs index 67d6b4c..643c00b 100644 --- a/OnixLabs.Core/ArrayExtensions.cs +++ b/OnixLabs.Core/Extensions.Array.cs @@ -1,18 +1,17 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.ComponentModel; namespace OnixLabs.Core; @@ -24,47 +23,38 @@ namespace OnixLabs.Core; public static class ArrayExtensions { /// - /// Copies this array. + /// Creates a copy of the current . /// - /// The array to copy. + /// The current to copy. /// The underlying type of the array. - /// Returns an exact copy of this array. + /// Returns an exact copy of the current . public static T[] Copy(this T[] array) { - return Copy(array, 0, array.Length); + return [..array]; } /// - /// Copies this array. + /// Creates a copy of the current . /// - /// The array to copy. + /// The current to copy. /// The index of the array to begin copying from. /// The number of elements of the array to copy. /// The underlying type of the array. - /// Returns an exact copy of this array. + /// Returns an exact copy of the current . public static T[] Copy(this T[] array, int index, int count) { - T[] result = new T[count]; - Array.Copy(array, index, result, 0, count); - - return result; + return [..array[index..(index + count)]]; } /// - /// Concatenates this array with another array. + /// Concatenates the current with another . /// - /// The source array to concatenate with the other array. - /// The other array to concatenate with the source array. - /// The underlying type of the array. - /// Returns this array concatenated with the other array. + /// The source to concatenate with the other . + /// The other to concatenate with the source . + /// The underlying type of the . + /// Returns the current concatenated with the other . public static T[] ConcatenateWith(this T[] array, T[] other) { - int length = array.Length + other.Length; - T[] result = new T[length]; - - Array.Copy(array, 0, result, 0, array.Length); - Array.Copy(other, 0, result, array.Length, other.Length); - - return result; + return [..array, ..other]; } } diff --git a/OnixLabs.Core/Extensions.Object.cs b/OnixLabs.Core/Extensions.Object.cs new file mode 100644 index 0000000..8005057 --- /dev/null +++ b/OnixLabs.Core/Extensions.Object.cs @@ -0,0 +1,60 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using OnixLabs.Core.Reflection; + +namespace OnixLabs.Core; + +/// +/// Provides extension methods for objects. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class ObjectExtensions +{ + /// + /// Compares the current instance with the specified instance. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// The underlying type of the current . + /// Returns a signed integer that indicates the relative order of the objects being compared. + /// If the specified object is not , or of the specified type. + public static int CompareToObject(this IComparable comparable, object? obj) + { + if (obj is null) return 1; + if (obj is T other) return comparable.CompareTo(other); + throw new ArgumentException($"Object must be of type {typeof(T).Name}", nameof(obj)); + } + + /// + /// Gets a record-like representation of the current instance. + /// + /// The current instance. + /// Returns a record-like representation of the current instance. + public static string ToRecordString(this object value) + { + Type type = value.GetType(); + + IEnumerable properties = type + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Select(property => $"{property.Name} = {property.GetValue(value)}"); + + return $"{type.GetName()} {{ {string.Join(", ", properties)} }}"; + } +} diff --git a/OnixLabs.Core/Extensions.String.cs b/OnixLabs.Core/Extensions.String.cs new file mode 100644 index 0000000..4a92b91 --- /dev/null +++ b/OnixLabs.Core/Extensions.String.cs @@ -0,0 +1,349 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace OnixLabs.Core; + +/// +/// Provides extension methods for strings. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class StringExtensions +{ + /// + /// The value of an index not found. + /// + private const int NotFound = -1; + + /// + /// The default string comparison. + /// + private const StringComparison DefaultComparison = StringComparison.Ordinal; + + /// + /// The default date/time styles. + /// + private const DateTimeStyles DefaultStyles = DateTimeStyles.None; + + /// + /// The default encoding. + /// + private static readonly Encoding DefaultEncoding = Encoding.UTF8; + + /// + /// Repeats the current by the specified number of repetitions. + /// + /// The instance to repeat. + /// The number of repetitions of the current instance. + /// Returns a new instance representing the repetition of the current instance. + public static string Repeat(this string value, int count) + { + return count > 0 ? string.Join(string.Empty, Enumerable.Repeat(value, count)) : string.Empty; + } + + /// + /// Obtains a sub-string before the specified index within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The index in the current from which to obtain a sub-string. + /// + /// The value to return in the event that the index is less than zero. + /// If the default value is then the current instance is returned. + /// + /// + /// Returns a sub-string before the specified index. If the index is less than zero, then the default value will be returned. + /// If the default value is , then the current instance will be returned. + /// + private static string SubstringBeforeIndex(this string value, int index, string? defaultValue = null) + { + return index <= NotFound ? defaultValue ?? value : value[..index]; + } + + /// + /// Obtains a sub-string after the specified index within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The index in the current from which to obtain a sub-string. + /// The offset from the index which marks the beginning of the sub-string. + /// + /// The value to return in the event that the index is less than zero. + /// If the default value is then the current instance is returned. + /// + /// + /// Returns a sub-string after the specified index. If the index is less than zero, then the default value will be returned. + /// If the default value is , then the current instance will be returned. + /// + private static string SubstringAfterIndex(this string value, int index, int offset, string? defaultValue = null) + { + return index <= NotFound ? defaultValue ?? value : value[(index + offset)..value.Length]; + } + + /// + /// Obtains a sub-string before the first occurrence of the specified delimiter within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The delimiter to find within the current instance. + /// + /// The value to return in the event that the delimiter is not found in the current instance. + /// If the default value is then the current instance is returned. + /// + /// + /// Returns a sub-string before the first occurrence of the specified delimiter within the current instance. + /// If the delimiter is not found, then the default value will be returned. + /// If the default value is , then the current instance is returned. + /// + public static string SubstringBeforeFirst(this string value, char delimiter, string? defaultValue = null) + { + return value.SubstringBeforeIndex(value.IndexOf(delimiter), defaultValue); + } + + /// + /// Obtains a sub-string before the first occurrence of the specified delimiter within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The delimiter to find within the current instance. + /// + /// The value to return in the event that the delimiter is not found in the current instance. + /// If the default value is then the current instance is returned. + /// + /// + /// The culture, case and sort rules that will be applied when searching for the specified delimiter. + /// The default value is . + /// + /// + ///Returns a sub-string before the first occurrence of the specified delimiter within the current instance. + /// If the delimiter is not found, then the default value will be returned. + /// If the default value is , then the current instance is returned. + /// + public static string SubstringBeforeFirst(this string value, string delimiter, string? defaultValue = null, StringComparison comparison = DefaultComparison) + { + return value.SubstringBeforeIndex(value.IndexOf(delimiter, comparison), defaultValue); + } + + /// + /// Obtains a sub-string before the last occurrence of the specified delimiter within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The delimiter to find within the current instance. + /// + /// The value to return in the event that the delimiter is not found in the current instance. + /// If the default value is then the current instance is returned. + /// + /// + ///Returns a sub-string before the last occurrence of the specified delimiter within the current instance. + /// If the delimiter is not found, then the default value will be returned. + /// If the default value is , then the current instance is returned. + /// + public static string SubstringBeforeLast(this string value, char delimiter, string? defaultValue = null) + { + return value.SubstringBeforeIndex(value.LastIndexOf(delimiter), defaultValue); + } + + /// + /// Obtains a sub-string before the last occurrence of the specified delimiter within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The delimiter to find within the current instance. + /// + /// The value to return in the event that the delimiter is not found in the current instance. + /// If the default value is then the current instance is returned. + /// + /// + /// The culture, case and sort rules that will be applied when searching for the specified delimiter. + /// The default value is . + /// + /// + ///Returns a sub-string before the last occurrence of the specified delimiter within the current instance. + /// If the delimiter is not found, then the default value will be returned. + /// If the default value is , then the current instance is returned. + /// + public static string SubstringBeforeLast(this string value, string delimiter, string? defaultValue = null, StringComparison comparison = DefaultComparison) + { + return value.SubstringBeforeIndex(value.LastIndexOf(delimiter, comparison), defaultValue); + } + + /// + /// Obtains a sub-string after the first occurrence of the specified delimiter within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The delimiter to find within the current instance. + /// + /// The value to return in the event that the delimiter is not found in the current instance. + /// If the default value is then the current instance is returned. + /// + /// + /// Returns a sub-string after the first occurrence of the specified delimiter within the current instance. + /// If the delimiter is not found, then the default value will be returned. + /// If the default value is , then the current instance is returned. + /// + public static string SubstringAfterFirst(this string value, char delimiter, string? defaultValue = null) + { + return value.SubstringAfterIndex(value.IndexOf(delimiter), 1, defaultValue); + } + + /// + /// Obtains a sub-string after the first occurrence of the specified delimiter within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The delimiter to find within the current instance. + /// + /// The value to return in the event that the delimiter is not found in the current instance. + /// If the default value is then the current instance is returned. + /// + /// + /// The culture, case and sort rules that will be applied when searching for the specified delimiter. + /// The default value is . + /// + /// + ///Returns a sub-string after the first occurrence of the specified delimiter within the current instance. + /// If the delimiter is not found, then the default value will be returned. + /// If the default value is , then the current instance is returned. + /// + public static string SubstringAfterFirst(this string value, string delimiter, string? defaultValue = null, StringComparison comparison = DefaultComparison) + { + return value.SubstringAfterIndex(value.IndexOf(delimiter, comparison), delimiter.Length, defaultValue); + } + + /// + /// Obtains a sub-string after the last occurrence of the specified delimiter within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The delimiter to find within the current instance. + /// + /// The value to return in the event that the delimiter is not found in the current instance. + /// If the default value is then the current instance is returned. + /// + /// + ///Returns a sub-string after the last occurrence of the specified delimiter within the current instance. + /// If the delimiter is not found, then the default value will be returned. + /// If the default value is , then the current instance is returned. + /// + public static string SubstringAfterLast(this string value, char delimiter, string? defaultValue = null) + { + return value.SubstringAfterIndex(value.LastIndexOf(delimiter), 1, defaultValue); + } + + /// + /// Obtains a sub-string after the last occurrence of the specified delimiter within the current instance. + /// + /// The current instance from which to obtain a sub-string. + /// The delimiter to find within the current instance. + /// + /// The value to return in the event that the delimiter is not found in the current instance. + /// If the default value is then the current instance is returned. + /// + /// + /// The culture, case and sort rules that will be applied when searching for the specified delimiter. + /// The default value is . + /// + /// + ///Returns a sub-string after the last occurrence of the specified delimiter within the current instance. + /// If the delimiter is not found, then the default value will be returned. + /// If the default value is , then the current instance is returned. + /// + public static string SubstringAfterLast(this string value, string delimiter, string? defaultValue = null, StringComparison comparison = DefaultComparison) + { + return value.SubstringAfterIndex(value.LastIndexOf(delimiter, comparison), 1, defaultValue); + } + + /// + /// Converts the current instance into a new instance. + /// + /// The current from which to obtain a . + /// The encoding that will be used to convert the current into a . + /// Returns a new representation of the current instance. + public static byte[] ToByteArray(this string value, Encoding? encoding = null) + { + return (encoding ?? DefaultEncoding).GetBytes(value); + } + + /// + /// Converts the current instance into a new instance. + /// + /// The current instance to convert. + /// An object that provides culture-specific formatting information. + /// A bit-wise combination of enumeration values that indicate the style elements that can be present. + /// Returns a new instance parsed from the current instance. + public static DateTime ToDateTime(this string value, IFormatProvider? provider = null, DateTimeStyles styles = DefaultStyles) + { + return DateTime.Parse(value, provider, styles); + } + + /// + /// Converts the current instance into a new instance. + /// + /// The current instance to convert. + /// An object that provides culture-specific formatting information. + /// A bit-wise combination of enumeration values that indicate the style elements that can be present. + /// Returns a new instance parsed from the current instance. + public static DateOnly ToDateOnly(this string value, IFormatProvider? provider = null, DateTimeStyles styles = DefaultStyles) + { + return DateOnly.Parse(value, provider, styles); + } + + /// + /// Converts the current instance into a new instance. + /// + /// The current instance to convert. + /// An object that provides culture-specific formatting information. + /// A bit-wise combination of enumeration values that indicate the style elements that can be present. + /// Returns a new instance parsed from the current instance. + public static TimeOnly ToTimeOnly(this string value, IFormatProvider? provider = null, DateTimeStyles styles = DefaultStyles) + { + return TimeOnly.Parse(value, provider, styles); + } + + /// + /// Tries to copy the current instance into the destination . + /// + /// The current instance to copy. + /// The into which the current contents will be copied. + /// The number of characters written to the destination . + /// Returns if the current instance was copied into the destination ; otherwise, if the destination was too short. + public static bool TryCopyTo(this string value, Span destination, out int charsWritten) + { + bool result = value.TryCopyTo(destination); + charsWritten = result ? value.Length : 0; + return result; + } + + /// + /// Wraps the current instance between the specified before and after instances. + /// + /// The current instance to wrap. + /// The that should precede the current instance. + /// The that should succeed the current instance. + /// Returns a new instance representing the current instance, wrapped between the specified before and after instances. + public static string Wrap(this string value, char before, char after) + { + return $"{before}{value}{after}"; + } + + /// + /// Wraps the current instance between the specified before and after instances. + /// + /// The current instance to wrap. + /// The that should precede the current instance. + /// The that should succeed the current instance. + /// Returns a new instance representing the current instance, wrapped between the specified before and after instances. + public static string Wrap(this string value, string before, string after) + { + return $"{before}{value}{after}"; + } +} diff --git a/OnixLabs.Core/HashCodeExtensions.cs b/OnixLabs.Core/HashCodeExtensions.cs deleted file mode 100644 index b22c40f..0000000 --- a/OnixLabs.Core/HashCodeExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using OnixLabs.Core.Linq; - -namespace OnixLabs.Core; - -/// -/// Provides extension methods for hash codes. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static class HashCodeExtensions -{ - /// - /// Adds an item to be hashed into a instance. - /// - /// The which will receive the item to hash. - /// The item to hash into the . - /// The underlying type of the item to hash. - /// Returns the containing the added item. - public static HashCode AddItem(this HashCode hashCode, T item) - { - hashCode.Add(item); - return hashCode; - } - - /// - /// Adds the items to be hashed into a instance. - /// - /// The which will receive the items to hash. - /// The items to hash into the . - /// The underlying type of the items to hash. - /// Returns the containing the added items. - public static HashCode AddItems(this HashCode hashCode, IEnumerable items) - { - items.ForEach(hashCode.Add); - return hashCode; - } -} diff --git a/OnixLabs.Core/IBinaryConvertible.cs b/OnixLabs.Core/IBinaryConvertible.cs new file mode 100644 index 0000000..a6bace1 --- /dev/null +++ b/OnixLabs.Core/IBinaryConvertible.cs @@ -0,0 +1,27 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Core; + +/// +/// Defines a type that is convertible to a binary representation. +/// +public interface IBinaryConvertible +{ + /// + /// Gets the underlying representation of the current instance. + /// + /// Return the underlying representation of the current instance. + byte[] ToByteArray(); +} diff --git a/OnixLabs.Core/IValueComparable.cs b/OnixLabs.Core/IValueComparable.cs new file mode 100644 index 0000000..921fdeb --- /dev/null +++ b/OnixLabs.Core/IValueComparable.cs @@ -0,0 +1,73 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core; + +/// +/// Defines an extension to the default and interfaces, +/// including equality, inequality, greater than, greater than or equal, less than, and less than or equal operators. +/// +/// The underlying type of the objects to compare. +public interface IValueComparable : IComparable, IComparable where T : IValueComparable +{ + /// + /// Performs an equality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static abstract bool operator ==(T left, T right); + + /// + /// Performs an inequality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static abstract bool operator !=(T left, T right); + + /// + /// Performs a greater than comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is greater than the right-hand instance; otherwise, . + public static abstract bool operator >(T left, T right); + + /// + /// Performs a greater than or equal comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is greater than or equal to the right-hand instance; otherwise, . + public static abstract bool operator >=(T left, T right); + + /// + /// Performs a less than comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is less than the right-hand instance; otherwise, . + public static abstract bool operator <(T left, T right); + + /// + /// Performs a less than or equal comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is less than or equal to the right-hand instance; otherwise, . + public static abstract bool operator <=(T left, T right); +} diff --git a/OnixLabs.Core/IValueEquatable.cs b/OnixLabs.Core/IValueEquatable.cs new file mode 100644 index 0000000..eafb819 --- /dev/null +++ b/OnixLabs.Core/IValueEquatable.cs @@ -0,0 +1,40 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core; + +/// +/// Defines an extension to the default interface, including equality and inequality operators. +/// +/// The underlying type of the objects to compare. +public interface IValueEquatable : IEquatable where T : IValueEquatable +{ + /// + /// Performs an equality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static abstract bool operator ==(T left, T right); + + /// + /// Performs an inequality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static abstract bool operator !=(T left, T right); +} diff --git a/OnixLabs.Core/Linq/Extensions.IEnumerable.cs b/OnixLabs.Core/Linq/Extensions.IEnumerable.cs new file mode 100644 index 0000000..41b7ace --- /dev/null +++ b/OnixLabs.Core/Linq/Extensions.IEnumerable.cs @@ -0,0 +1,244 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Numerics; + +namespace OnixLabs.Core.Linq; + +/// +/// Provides LINQ-like extension methods for . +/// +// ReSharper disable InconsistentNaming +[EditorBrowsable(EditorBrowsableState.Never)] +public static class IEnumerableExtensions +{ + /// + /// Determines whether all elements of the current are equal by a specified property. + /// + /// The current on which to perform the operation. + /// The selector function which will be used to select the desired property from each element. + /// The underlying type of the . + /// The underlying type of each selected element. + /// Returns if all selected element properties are equal; otherwise . + public static bool AllEqualBy(this IEnumerable enumerable, Func selector) + { + return enumerable.Select(selector).Distinct().IsSingle(); + } + + /// + /// Determines whether any elements of the current are equal by a specified property. + /// + /// The current on which to perform the operation. + /// The selector function which will be used to select the desired property from each element. + /// The underlying type of the . + /// The underlying type of each selected element. + /// Returns if any selected element properties are equal; otherwise . + public static bool AnyEqualBy(this IEnumerable enumerable, Func selector) + { + return enumerable.GroupBy(selector).Any(group => group.Count() > 1); + } + + /// + /// Obtains a number that represents how many elements in the current do not satisfy the specified predicate condition. + /// + /// The current on which to perform the operation. + /// The function to test each element for a condition. + /// The underlying type of the . + /// Returns a number that represents how many elements in the current do not satisfy the specified predicate condition. + public static int CountNot(this IEnumerable enumerable, Func predicate) + { + return enumerable.Count(element => !predicate(element)); + } + + /// + /// Performs the specified for each element of the current . + /// + /// The current over which to iterate. + /// The to perform for each element. + /// The underlying type of the . + public static void ForEach(this IEnumerable enumerable, Action action) + { + foreach (T element in enumerable) action(element); + } + + /// + /// Gets the content hash code of the elements of the current . + /// + /// The current from which to compute a content hash code. + /// The underlying type of the . + /// Returns the computed content hash code of the current . + public static int GetContentHashCode(this IEnumerable enumerable) + { + HashCode result = new(); + enumerable.ForEach(element => result.Add(element)); + return result.ToHashCode(); + } + + /// + /// Determines whether the current is empty. + /// + /// The current on which to perform the operation. + /// The underlying type of the . + /// Returns if the is empty; otherwise, . + public static bool IsEmpty(this IEnumerable enumerable) + { + return !enumerable.Any(); + } + + /// + /// Determines whether the current is not empty. + /// + /// The current on which to perform the operation. + /// The underlying type of the . + /// Returns if the is not empty; otherwise, . + public static bool IsNotEmpty(this IEnumerable enumerable) + { + return enumerable.Any(); + } + + /// + /// Determines whether the current contains a single element. + /// + /// The current on which to perform the operation. + /// The underlying type of the . + /// Returns if the contains a single element; otherwise, . + public static bool IsSingle(this IEnumerable enumerable) + { + return enumerable.LongCount() == 1; + } + + /// + /// Determines whether the current contains an even number of elements. + /// + /// The current on which to perform the operation. + /// The function to test each element for a condition. + /// The underlying type of the . + /// Returns if the contains an even number of elements; otherwise, . + public static bool IsCountEven(this IEnumerable enumerable, Func? predicate = null) + { + return enumerable.LongCount(element => predicate?.Invoke(element) ?? true) % 2 == 0; + } + + /// + /// Determines whether the current contains an odd number of elements. + /// + /// The current on which to perform the operation. + /// The function to test each element for a condition. + /// The underlying type of the . + /// Returns if the contains an odd number of elements; otherwise, . + public static bool IsCountOdd(this IEnumerable enumerable, Func? predicate = null) + { + return enumerable.LongCount(element => predicate?.Invoke(element) ?? true) % 2 != 0; + } + + /// + /// Joins the elements of the current into a instance. + /// + /// The current to join. + /// The separator which will appear between joined elements. + /// The underlying type of the . + /// Returns the elements of the current , joined into a instance. + public static string JoinToString(this IEnumerable enumerable, string separator = ", ") + { + return string.Join(separator, enumerable); + } + + /// + /// Determines whether none of the elements of the current satisfy the specified predicate condition. + /// + /// The current on which to perform the operation. + /// The function to test each element for a condition. + /// The underlying type of the . + /// Returns if none of the elements of the current satisfy the specified predicate condition; otherwise, . + public static bool None(this IEnumerable enumerable, Func predicate) + { + return !enumerable.Any(predicate); + } + + /// + /// Calculates the sum of the elements of the current . + /// + /// The current to sum. + /// The underlying type of the . + /// Returns the sum of the elements of the current . + public static T Sum(this IEnumerable enumerable) where T : INumberBase + { + IEnumerable numbers = enumerable as T[] ?? enumerable.ToArray(); + return numbers.IsEmpty() ? T.Zero : numbers.Aggregate((left, right) => left + right); + } + + /// + /// Calculates the sum of the elements of the current . + /// + /// The current to sum. + /// The selector function which will be used to select each property from each element. + /// The underlying type of the . + /// The underlying type of each element to sum. + /// Returns the sum of the elements of the current . + public static TResult SumBy(this IEnumerable enumerable, Func selector) where TResult : INumberBase + { + return enumerable.Select(selector).Sum(); + } + + /// + /// Filters the current elements that do not satisfy the specified predicate condition. + /// + /// The current on which to perform the operation. + /// The function to test each element for a condition. + /// The underlying type of the . + /// Returns a new that contains elements that do not satisfy the condition. + public static IEnumerable WhereNot(this IEnumerable enumerable, Func predicate) + { + return enumerable.Where(element => !predicate(element)); + } + + /// + /// Filters the current elements that are not . + /// + /// The current on which to perform the operation. + /// The underlying type of the . + /// Returns a new that contains elements that are not . + public static IEnumerable WhereNotNull(this IEnumerable enumerable) where T : notnull + { + return enumerable.Where(element => element is not null)!; + } + + /// + /// Formats the current as a collection string. + /// + /// The current to format. + /// The underlying type of the . + /// Returns a new instance representing the current . + public static string ToCollectionString(this IEnumerable enumerable) + { + return enumerable.JoinToString().Wrap('[', ']'); + } + + /// + /// Formats the current as a collection string. + /// + /// The current to format. + /// The format which will be applied to each element. + /// An object that provides culture-specific formatting information. + /// The underlying type of the . + /// Returns a new instance representing the current . + public static string ToCollectionString(this IEnumerable enumerable, string format, IFormatProvider? provider = null) where T : IFormattable + { + return enumerable.Select(element => element.ToString(format, provider)).ToCollectionString(); + } +} diff --git a/OnixLabs.Core/Linq/IEnumerableExtensions.cs b/OnixLabs.Core/Linq/IEnumerableExtensions.cs deleted file mode 100644 index d082232..0000000 --- a/OnixLabs.Core/Linq/IEnumerableExtensions.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// ReSharper disable InconsistentNaming - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; - -namespace OnixLabs.Core.Linq; - -/// -/// Provides LINQ-like extension methods for . -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static class IEnumerableExtensions -{ - /// - /// Determines whether all elements of this are equal by a specified property. - /// - /// The on which to perform the operation. - /// The selector function which will be used to select each property from each element. - /// The underlying type of the . - /// The underlying type of each selected element. - /// Returns true if all selected element properties are equal; otherwise false. - public static bool AllEqualBy(this IEnumerable enumerable, Func selector) - { - IEnumerable elements = enumerable.ToArray(); - - if (elements.IsEmpty()) - { - return false; - } - - if (elements.IsSingle()) - { - return true; - } - - TProperty first = selector(elements.First()); - return elements.All(element => Equals(first, selector(element))); - } - - /// - /// Determines whether any elements of this are equal by a specified property. - /// - /// The on which to perform the operation. - /// The selector function which will be used to select each property from each element. - /// The underlying type of the . - /// The underlying type of each selected element. - /// Returns true if any selected element properties are equal; otherwise false. - public static bool AnyEqualBy(this IEnumerable enumerable, Func selector) - { - IEnumerable elements = enumerable.ToArray(); - - if (elements.IsEmpty()) - { - return false; - } - - if (elements.IsSingle()) - { - return true; - } - - TProperty first = selector(elements.First()); - return elements.Any(element => Equals(first, selector(element))); - } - - /// - /// Performs the specified for each element of this . - /// - /// The over which to iterate. - /// The to perform for each element. - /// The underlying type of the . - public static void ForEach(this IEnumerable enumerable, Action action) - { - foreach (T element in enumerable) - { - action(element); - } - } - - /// - /// Gets the content hash code of the elements of this . - /// - /// The from which to compute a content hash code. - /// The underlying type of the . - /// Returns the computed content hash code of this . - public static int GetContentHashCode(this IEnumerable enumerable) - { - return new HashCode().AddItems(enumerable).ToHashCode(); - } - - /// - /// Determines whether this is empty. - /// - /// The on which to perform the operation. - /// he underlying type of the . - /// Returns true if the is empty; otherwise, false. - public static bool IsEmpty(this IEnumerable enumerable) - { - return !enumerable.Any(); - } - - /// - /// Determines whether this is not empty. - /// - /// The on which to perform the operation. - /// The underlying type of the . - /// Returns true if the is not empty; otherwise, false. - public static bool IsNotEmpty(this IEnumerable enumerable) - { - return !enumerable.IsEmpty(); - } - - /// - /// Determines whether this contains a single element. - /// - /// The on which to perform the operation. - /// The underlying type of the . - /// Returns true if the contains a single element; otherwise, false. - public static bool IsSingle(this IEnumerable enumerable) - { - return enumerable.LongCount() == 1; - } - - /// - /// Determines whether this contains an even number of elements. - /// - /// The on which to perform the operation. - /// The underlying type of the . - /// Returns true if the contains an even number of elements; otherwise, false. - public static bool IsCountEven(this IEnumerable enumerable) - { - return enumerable.LongCount() % 2 == 0; - } - - /// - /// Determines whether this contains an odd number of elements. - /// - /// The on which to perform the operation. - /// The underlying type of the . - /// Returns true if the contains an odd number of elements; otherwise, false. - public static bool IsCountOdd(this IEnumerable enumerable) - { - return !enumerable.IsCountEven(); - } - - /// - /// Filters this to only elements of the specified type. - /// - /// The on which to perform the operation. - /// The underlying type of the . - /// Returns a new containing only elements of the specified type. - public static IEnumerable WhereIs(this IEnumerable enumerable) - { - foreach (object? element in enumerable) - { - if (element is T elementOfType) - { - yield return elementOfType; - } - } - } -} diff --git a/OnixLabs.Core/OnixLabs.Core.csproj b/OnixLabs.Core/OnixLabs.Core.csproj index 69d849f..d9e77e1 100644 --- a/OnixLabs.Core/OnixLabs.Core.csproj +++ b/OnixLabs.Core/OnixLabs.Core.csproj @@ -1,28 +1,45 @@ - - net6.0 - 10 + net8.0 + 12 enable true OnixLabs.Core ONIXLabs ONIXLabs Core API for .NET - 5.0.0 + 7.0.0 en - Copyright © ONIXLabs 2020-2022 + Copyright © ONIXLabs 2020 https://github.com/onix-labs/onixlabs-dotnet - 5.0.0 + 7.0.0 - - - true - bin\Debug\net5.0\OnixLabs.Core.xml + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + embedded + true + true + true + Apache-2.0 + https://github.com/onix-labs/onixlabs-dotnet + README.md + true + true + git + https://github.com/onix-labs/onixlabs-dotnet - - - bin\Release\net5.0\OnixLabs.Core.xml - true + + true - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/OnixLabs.Core/Preconditions.cs b/OnixLabs.Core/Preconditions.cs index e38c4ac..e149ef1 100644 --- a/OnixLabs.Core/Preconditions.cs +++ b/OnixLabs.Core/Preconditions.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,47 +13,88 @@ // limitations under the License. using System; -using System.Runtime.CompilerServices; +using OnixLabs.Core.Linq; namespace OnixLabs.Core; /// -/// Provides methods for performing pre-conditions and guard clauses. +/// Provides general methods for performing pre-conditions and guard clauses. /// public static class Preconditions { /// - /// Performs a general pre-condition check, which fails if the condition returns false. + /// Performs a general pre-condition check that fails when the specified condition is . /// - /// The condition to check. - /// The exception message to throw in the event that the condition fails. - /// The name of the parameter which is invalid. - /// If the condition fails. - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static void Check(bool value, string message = "Argument check failed.", string? paramName = null) + /// The condition to check. + /// The exception message to throw in the event that the specified condition is . + /// If the specified condition is . + public static void Check(bool condition, string message = "Check failed.") { - if (!value) - { - throw new ArgumentException(message, paramName); - } + if (!condition) throw new InvalidOperationException(message); } /// - /// Performs a general pre-condition check that the specified value is not null, which fails if the value is null. + /// Performs a general pre-condition check that fails if the specified value is . /// /// The nullable value to check. - /// The exception message to throw in the event that the condition fails. - /// The underlying type of the value to check. - /// Returns the specified value as non-nullable in the event that the value is not null. - /// If the condition fails and the value is null. - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static T CheckNotNull(T? value, string message = "Argument null checked failed.") where T : notnull + /// The exception message to throw in the event that the specified value is . + /// The underlying type of the value. + /// Returns a non-null value of the specified type. + /// If the specified value is . + public static T CheckNotNull(T? value, string message = "Argument must not be null.") where T : notnull { - if (value is null) - { - throw new ArgumentNullException(message); - } + return value ?? throw new InvalidOperationException(message); + } + + /// + /// Performs a general pre-condition requirement that fails when the specified condition is . + /// + /// The condition to check. + /// The exception message to throw in the event that the specified condition is . + /// The name of the invalid paramter. + /// If the specified condition is . + public static void Require(bool condition, string message = "Argument requirement failed.", string? parameterName = null) + { + if (!condition) throw new ArgumentException(message, parameterName); + } + + /// + /// Performs a general pre-condition requirement that fails when the specified condition is . + /// + /// The condition to check. + /// The exception message to throw in the event that the specified condition is . + /// The name of the invalid paramter. + /// If the specified condition is . + public static void RequireWithinRange(bool condition, string message = "Argument is out of range.", string? parameterName = null) + { + if (!condition) throw new ArgumentOutOfRangeException(parameterName, message); + } + + /// + /// Performs a general pre-condition requirement that fails if the specified value is . + /// + /// The nullable value to check. + /// The exception message to throw in the event that the specified value is . + /// The name of the invalid parameter. + /// The underlying type of the value. + /// Returns a non-null value of the specified type. + /// If the specified value is . + public static T RequireNotNull(T? value, string message = "Argument must not be null.", string? parameterName = null) where T : notnull + { + return value ?? throw new ArgumentNullException(parameterName, message); + } + + /// + /// Performs a general pre-condition requirement that the specified value is defined by the specified type. + /// + /// The enum value to check. + /// The name of the invalid parameter. + /// The underlying type of the . + public static void RequireIsDefined(T value, string? parameterName = null) where T : struct, Enum + { + if (Enum.IsDefined(value)) return; - return value; + string message = $"Invalid {typeof(T).Name} enum value: {value}. Valid values include: {Enum.GetNames().JoinToString()}."; + throw new ArgumentOutOfRangeException(parameterName, message); } } diff --git a/OnixLabs.Core/Reflection/Extensions.Type.cs b/OnixLabs.Core/Reflection/Extensions.Type.cs new file mode 100644 index 0000000..7ca3965 --- /dev/null +++ b/OnixLabs.Core/Reflection/Extensions.Type.cs @@ -0,0 +1,40 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.ComponentModel; + +namespace OnixLabs.Core.Reflection; + +/// +/// Provides extension methods for types. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class TypeExtensions +{ + /// + /// The identifier marker than indicates a generic type. + /// + private const char GenericTypeIdentifierMarker = '`'; + + /// + /// Gets the simple type name from the current instance. + /// + /// The current instance from which to obtain the simple name. + /// Returns the simple type name from the current instance. + public static string GetName(this Type type) + { + return type.Name.SubstringBeforeFirst(GenericTypeIdentifierMarker); + } +} diff --git a/OnixLabs.Core/StringExtensions.cs b/OnixLabs.Core/StringExtensions.cs deleted file mode 100644 index 6be0262..0000000 --- a/OnixLabs.Core/StringExtensions.cs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.ComponentModel; -using System.Text; - -namespace OnixLabs.Core; - -/// -/// Provides extension methods for strings. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static class StringExtensions -{ - /// - /// The value of an index not found. - /// - private const int IndexNotFound = -1; - - /// - /// Returns a substring before the first occurrence of the specified delimiter. - /// - /// The original from which to obtain a sub-string. - /// The delimiter to find within the original string. - /// The value to return if the delimiter is not found, which defaults to the original string. - /// Returns a substring before the first occurrence of the specified delimiter. - public static string SubstringBefore(this string value, char delimiter, string? defaultValue = null) - { - int index = value.IndexOf(delimiter); - - return index switch - { - IndexNotFound => defaultValue ?? value, - _ => value[..index] - }; - } - - /// - /// Returns a substring before the first occurrence of the specified delimiter. - /// - /// The original from which to obtain a sub-string. - /// The delimiter to find within the original string. - /// The value to return if the delimiter is not found, which defaults to the original string. - /// Specifies the string comparison rule for the search. - /// Returns a substring before the first occurrence of the specified delimiter. - public static string SubstringBefore( - this string value, - string delimiter, - string? defaultValue = null, - StringComparison comparison = StringComparison.Ordinal) - { - int index = value.IndexOf(delimiter, comparison); - - return index switch - { - IndexNotFound => defaultValue ?? value, - _ => value[..index] - }; - } - - /// - /// Returns a substring before the last occurrence of the specified delimiter. - /// - /// The original from which to obtain a sub-string. - /// The delimiter to find within the original string. - /// The value to return if the delimiter is not found, which defaults to the original string. - /// Returns a substring before the last occurrence of the specified delimiter. - public static string SubstringBeforeLast(this string value, char delimiter, string? defaultValue = null) - { - int index = value.LastIndexOf(delimiter); - - return index switch - { - IndexNotFound => defaultValue ?? value, - _ => value[..index] - }; - } - - /// - /// Returns a substring before the last occurrence of the specified delimiter. - /// - /// The original from which to obtain a sub-string. - /// The delimiter to find within the original string. - /// The value to return if the delimiter is not found, which defaults to the original string. - /// Specifies the string comparison rule for the search. - /// Returns a substring before the last occurrence of the specified delimiter. - public static string SubstringBeforeLast( - this string value, - string delimiter, - string? defaultValue = null, - StringComparison comparison = StringComparison.Ordinal) - { - int index = value.LastIndexOf(delimiter, comparison); - - return index switch - { - IndexNotFound => defaultValue ?? value, - _ => value[..index] - }; - } - - /// - /// Returns a substring after the first occurrence of the specified delimiter. - /// - /// The original from which to obtain a sub-string. - /// The delimiter to find within the original string. - /// The value to return if the delimiter is not found, which defaults to the original string. - /// Returns a substring after the first occurrence of the specified delimiter. - public static string SubstringAfter(this string value, char delimiter, string? defaultValue = null) - { - int index = value.IndexOf(delimiter); - - return index switch - { - IndexNotFound => defaultValue ?? value, - _ => value[(index + 1)..value.Length] - }; - } - - /// - /// Returns a substring after the first occurrence of the specified delimiter. - /// - /// The original from which to obtain a sub-string. - /// The delimiter to find within the original string. - /// The value to return if the delimiter is not found, which defaults to the original string. - /// Specifies the string comparison rule for the search. - /// Returns a substring after the first occurrence of the specified delimiter. - public static string SubstringAfter( - this string value, - string delimiter, - string? defaultValue = null, - StringComparison comparison = StringComparison.Ordinal) - { - int index = value.IndexOf(delimiter, comparison); - - return index switch - { - IndexNotFound => defaultValue ?? value, - _ => value[(index + delimiter.Length)..value.Length] - }; - } - - /// - /// Returns a substring after the last occurrence of the specified delimiter. - /// - /// The original from which to obtain a sub-string. - /// The delimiter to find within the original string. - /// The value to return if the delimiter is not found, which defaults to the original string. - /// Returns a substring after the last occurrence of the specified delimiter. - public static string SubstringAfterLast(this string value, char delimiter, string? defaultValue = null) - { - int index = value.LastIndexOf(delimiter); - - return index switch - { - IndexNotFound => defaultValue ?? value, - _ => value[(index + 1)..value.Length] - }; - } - - /// - /// Returns a substring after the last occurrence of the specified delimiter. - /// - /// The original from which to obtain a sub-string. - /// The delimiter to find within the original string. - /// The value to return if the delimiter is not found, which defaults to the original string. - /// Specifies the string comparison rule for the search. - /// Returns a substring after the last occurrence of the specified delimiter. - public static string SubstringAfterLast( - this string value, - string delimiter, - string? defaultValue = null, - StringComparison comparison = StringComparison.Ordinal) - { - int index = value.LastIndexOf(delimiter, comparison); - - return index switch - { - IndexNotFound => defaultValue ?? value, - _ => value[(index + delimiter.Length)..value.Length] - }; - } - - /// - /// Converts the current to a array using the default . - /// - /// The original from which to obtain a byte array. - /// Returns a array using the default . - public static byte[] ToByteArray(this string value) - { - return ToByteArray(value, Encoding.Default); - } - - /// - /// Converts the current to a array using the specified . - /// - /// The original from which to obtain a byte array. - /// The which will be used to convert the current to a array. - /// Returns a array using the specified . - public static byte[] ToByteArray(this string value, Encoding encoding) - { - return encoding.GetBytes(value); - } - - /// - /// Converts the current between the specified before and after values. - /// - /// The current value to wrap. - /// The that should precede the current value. - /// The that should succeed the current value. - /// Returns the current wrapped between the specified before and after values. - public static string Wrap(this string value, string before, string after) - { - return $"{before}{value}{after}"; - } -} diff --git a/OnixLabs.Core/Text/Base16.Equatable.cs b/OnixLabs.Core/Text/Base16.Equatable.cs index e4d923f..dee52b2 100644 --- a/OnixLabs.Core/Text/Base16.Equatable.cs +++ b/OnixLabs.Core/Text/Base16.Equatable.cs @@ -1,73 +1,72 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Linq; using OnixLabs.Core.Linq; namespace OnixLabs.Core.Text; -public readonly partial struct Base16 : IEquatable +public readonly partial struct Base16 { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(Base16 a, Base16 b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(Base16 other) { - return Equals(a, b); + return other.value.SequenceEqual(value); } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(Base16 a, Base16 b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return obj is Base16 other && Equals(other); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public bool Equals(Base16 other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return other.Value.SequenceEqual(Value); + return value.GetContentHashCode(); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(Base16 left, Base16 right) { - return obj is Base16 other && Equals(other); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(Base16 left, Base16 right) { - return HashCode.Combine(Value.GetContentHashCode()); + return !Equals(left, right); } } diff --git a/OnixLabs.Core/Text/Base16.Format.cs b/OnixLabs.Core/Text/Base16.Format.cs new file mode 100644 index 0000000..ff7eb9f --- /dev/null +++ b/OnixLabs.Core/Text/Base16.Format.cs @@ -0,0 +1,33 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +public readonly partial struct Base16 +{ + /// + /// Tries to format the value of the current instance into the provided span of characters. + /// + /// The span in which to write this instance's value formatted as a span of characters. + /// When this method returns, contains the number of characters that were written in . + /// A span containing the characters that represent a standard or custom format string that defines the acceptable format for . + /// An optional object that supplies culture-specific formatting information for . + /// Returns if the formatting was successful; otherwise, . + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return ToString(format, provider).TryCopyTo(destination, out charsWritten); + } +} diff --git a/OnixLabs.Core/Text/Base16.From.cs b/OnixLabs.Core/Text/Base16.From.cs deleted file mode 100644 index 361de10..0000000 --- a/OnixLabs.Core/Text/Base16.From.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using System.Text; - -namespace OnixLabs.Core.Text; - -public readonly partial struct Base16 -{ - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Returns a new instance. - public static Base16 FromByteArray(byte[] value) - { - return new Base16(value); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Returns a new instance. - public static Base16 FromCharArray(char[] value) - { - return FromCharArray(value, Encoding.Default); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base16 FromCharArray(char[] value, Encoding encoding) - { - byte[] bytes = encoding.GetBytes(value); - return FromByteArray(bytes); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Returns a new instance. - public static Base16 FromSpan(ReadOnlySpan value) - { - return FromSpan(value, Encoding.Default); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base16 FromSpan(ReadOnlySpan value, Encoding encoding) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Returns a new instance. - public static Base16 FromString(string value) - { - return FromString(value, Encoding.Default); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base16 FromString(string value, Encoding encoding) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding); - } -} diff --git a/OnixLabs.Core/Text/Base16.Parse.cs b/OnixLabs.Core/Text/Base16.Parse.cs index 5ce2028..880eae8 100644 --- a/OnixLabs.Core/Text/Base16.Parse.cs +++ b/OnixLabs.Core/Text/Base16.Parse.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,35 +19,62 @@ namespace OnixLabs.Core.Text; public readonly partial struct Base16 { /// - /// Parses a Base-16 (hexadecimal) value into a instance. + /// Parses the specified Base-16 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// Returns a new instance. - public static Base16 Parse(string value) + /// The Base-16 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// Returns a new instance, parsed from the specified Base-16 encoded value. + public static Base16 Parse(string value, IFormatProvider? provider = null) { - ReadOnlySpan characters = value.AsSpan(); - return Parse(characters); + return Parse(value.AsSpan(), provider); } /// - /// Parses a Base-16 (hexadecimal) value into a instance. + /// Parses the specified Base-16 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// Returns a new instance. - public static Base16 Parse(char[] value) + /// The Base-16 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// Returns a new instance, parsed from the specified Base-16 encoded value. + public static Base16 Parse(ReadOnlySpan value, IFormatProvider? provider = null) { - ReadOnlySpan characters = value.AsSpan(); - return Parse(characters); + byte[] bytes = IBaseCodec.Base16.Decode(value, provider); + return new Base16(bytes); } /// - /// Parses a Base-16 (hexadecimal) value into a instance. + /// Tries to parse the specified Base-16 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// Returns a new instance. - public static Base16 Parse(ReadOnlySpan value) + /// The Base-16 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// + /// A new instance, parsed from the specified Base-16 encoded value, + /// or the default value if the specified Base-16 encoded could not be parsed. + /// + /// Returns if the specified Base-16 value was decoded successfully; otherwise, . + public static bool TryParse(string? value, IFormatProvider? provider, out Base16 result) { - byte[] bytes = Convert.FromHexString(value); - return FromByteArray(bytes); + return TryParse(value.AsSpan(), provider, out result); + } + + /// + /// Tries to parse the specified Base-16 encoded value into a value. + /// + /// The Base-16 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// + /// A new instance, parsed from the specified Base-16 encoded value, + /// or the default value if the specified Base-16 encoded could not be parsed. + /// + /// Returns if the specified Base-16 value was decoded successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, IFormatProvider? provider, out Base16 result) + { + if (IBaseCodec.Base16.TryDecode(value, provider, out byte[] bytes)) + { + result = new Base16(bytes); + return true; + } + + result = default; + return false; } } diff --git a/OnixLabs.Core/Text/Base16.To.cs b/OnixLabs.Core/Text/Base16.To.cs index f26ab6b..1cc48cd 100644 --- a/OnixLabs.Core/Text/Base16.To.cs +++ b/OnixLabs.Core/Text/Base16.To.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,46 +13,58 @@ // limitations under the License. using System; -using System.Text; namespace OnixLabs.Core.Text; public readonly partial struct Base16 { /// - /// Returns a array that represents the current object. + /// Gets the underlying representation of the current instance. /// - /// Returns a array that represents the current object. + /// Return the underlying representation of the current instance. public byte[] ToByteArray() { - return Value.Copy(); + return value.Copy(); } /// - /// Returns a that represents the current object in plain text. + /// Returns a that represents the current object. /// - /// Returns a that represents the current object in plain text. - public string ToPlainTextString() + /// Returns a that represents the current object. + public override string ToString() { - return ToPlainTextString(Encoding.Default); + return ToString(Base16FormatProvider.Invariant); } /// - /// Returns a that represents the current object in plain text. + /// Formats the value of the current instance using the specified format. /// - /// The encoding to use to obtain the underlying value. - /// Returns a that represents the current object in plain text. - public string ToPlainTextString(Encoding encoding) + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(IFormatProvider? formatProvider) { - return encoding.GetString(Value); + return ToString(null, formatProvider); } /// - /// Returns a that represents the current object. + /// Formats the value of the current instance using the specified format. /// - /// A that represents the current object. - public override string ToString() + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(format.AsSpan(), formatProvider); + } + + /// + /// Formats the value of the current instance using the specified format. + /// + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(ReadOnlySpan format, IFormatProvider? formatProvider) { - return Convert.ToHexString(Value).ToLower(); + return IBaseCodec.Base16.Encode(value, formatProvider); } } diff --git a/OnixLabs.Core/Text/Base16.cs b/OnixLabs.Core/Text/Base16.cs index bf6bbcc..eeba0e6 100644 --- a/OnixLabs.Core/Text/Base16.cs +++ b/OnixLabs.Core/Text/Base16.cs @@ -1,35 +1,33 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Core.Text; /// -/// Represents a Base-16 (hexadecimal) value. +/// Represents a Base-16 value. /// -public readonly partial struct Base16 +/// The underlying value. +public readonly partial struct Base16(ReadOnlySpan value) : IBaseValue { + private readonly byte[] value = value.ToArray(); + /// - /// Initializes a new instance of the struct. + /// Initializes a new default value. /// - /// The underlying value. - private Base16(byte[] value) + public Base16() : this([]) { - Value = value; } - - /// - /// Gets the underlying value. - /// - private byte[] Value { get; } } diff --git a/OnixLabs.Core/Text/Base16Codec.cs b/OnixLabs.Core/Text/Base16Codec.cs new file mode 100644 index 0000000..fcc49b4 --- /dev/null +++ b/OnixLabs.Core/Text/Base16Codec.cs @@ -0,0 +1,143 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Buffers; + +namespace OnixLabs.Core.Text; + +/// +/// Represents a codec for encoding and decoding Base-16 values. +/// +public sealed class Base16Codec : IBaseCodec +{ + private static readonly SearchValues Base16UppercaseValues = SearchValues.Create("ABCDEF"); + + private static readonly SearchValues Base16LowercaseValues = SearchValues.Create("abcdef"); + + /// + /// Encodes the specified value into a Base-16 representation. + /// + /// The value to encode into a Base-16 representation. + /// The format provider that will be used to encode the specified value. + /// Returns a new Base-16 representation encoded from the specified value. + public string Encode(ReadOnlySpan value, IFormatProvider? provider = null) + { + if (TryEncode(value, provider, out string result)) return result; + throw new FormatException(IBaseCodec.EncodingFormatException); + } + + /// + /// Decodes the specified Base-16 representation into a . + /// + /// The Base-16 value to decode into a . + /// The format provider that will be used to decode the specified value. + /// Returns a new decoded from the specified value. + public byte[] Decode(ReadOnlySpan value, IFormatProvider? provider = null) + { + if (TryDecode(value, provider, out byte[] result)) return result; + throw new FormatException(IBaseCodec.DecodingFormatException); + } + + /// + /// Tries to encode the specified value into a Base-16 representation. + /// + /// The value to encode into a Base-16 representation. + /// The format provider that will be used to encode the specified value. + /// + /// A new Base-16 representation encoded from the specified value, + /// or an empty string if the specified value could not be encoded. + /// + /// Returns if the specified value was encoded successfully; otherwise, . + public bool TryEncode(ReadOnlySpan value, IFormatProvider? provider, out string result) + { + try + { + if (value.IsEmpty) + { + result = string.Empty; + return true; + } + + if (provider is not null && provider is not Base16FormatProvider) + { + result = string.Empty; + return false; + } + + Base16FormatProvider formatProvider = provider as Base16FormatProvider ?? Base16FormatProvider.Invariant; + + result = formatProvider == Base16FormatProvider.Uppercase + ? Convert.ToHexString(value) + : Convert.ToHexString(value).ToLower(); + + return true; + } + catch + { + result = string.Empty; + return false; + } + } + + /// + /// Tries to decode the specified Base-16 representation into a . + /// + /// The Base-16 value to decode into a . + /// The format provider that will be used to decode the specified value. + /// + /// A new decoded from the specified value, + /// or an empty if the specified value could not be decoded. + /// + /// Returns if the specified value was decoded successfully; otherwise, . + public bool TryDecode(ReadOnlySpan value, IFormatProvider? provider, out byte[] result) + { + try + { + if (value.IsEmpty) + { + result = []; + return true; + } + + if (provider is not null && provider is not Base16FormatProvider) + { + result = []; + return false; + } + + Base16FormatProvider formatProvider = provider as Base16FormatProvider ?? Base16FormatProvider.Invariant; + + if (formatProvider == Base16FormatProvider.Uppercase && value.ContainsAny(Base16LowercaseValues)) + { + result = []; + return false; + } + + if (formatProvider == Base16FormatProvider.Lowercase && value.ContainsAny(Base16UppercaseValues)) + { + result = []; + return false; + } + + result = Convert.FromHexString(value); + return true; + } + catch + { + result = []; + return false; + } + } +} diff --git a/OnixLabs.Core/Text/Base16FormatProvider.cs b/OnixLabs.Core/Text/Base16FormatProvider.cs new file mode 100644 index 0000000..82541cf --- /dev/null +++ b/OnixLabs.Core/Text/Base16FormatProvider.cs @@ -0,0 +1,67 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +/// +/// Represents a Base-16 format provider. +/// +public sealed class Base16FormatProvider : Enumeration, IFormatProvider +{ + /// + /// Gets the invariant Base-16 format provider. + /// The invariant format provider favours lowercase for Base-16 encoding. + /// The alphabet provided by this format provider is not a strict Base-16 alphabet as it contains all uppercase and lowercase values. + /// + public static readonly Base16FormatProvider Invariant = new(0, nameof(Invariant), "0123456789ABCDEFabcdef"); + + /// + /// Gets the uppercase Base-16 format provider. + /// + public static readonly Base16FormatProvider Uppercase = new(1, nameof(Uppercase), "0123456789ABCDEF"); + + /// + /// Gets the lowercase Base-16 format provider. + /// + public static readonly Base16FormatProvider Lowercase = new(2, nameof(Lowercase), "0123456789abcdef"); + + /// + /// Initializes a new instance of the class. + /// + /// The value of the enumeration entry. + /// The name of the enumeration entry. + /// The alphabet of the format provider. + private Base16FormatProvider(int value, string name, string alphabet) : base(value, name) + { + Alphabet = alphabet; + } + + /// + /// Gets the alphabet of the current instance. + /// + public string Alphabet { get; } + + /// Gets an object that provides formatting services for the specified type. + /// An object that specifies the type of format object to return. + /// + /// Returns an instance of the object specified by , + /// if the implementation can supply that type of object; otherwise, . + /// + public object? GetFormat(Type? formatType) + { + return formatType == typeof(Base16FormatProvider) ? this : null; + } +} diff --git a/OnixLabs.Core/Text/Base32.Codec.cs b/OnixLabs.Core/Text/Base32.Codec.cs deleted file mode 100644 index 254c197..0000000 --- a/OnixLabs.Core/Text/Base32.Codec.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Text; - -namespace OnixLabs.Core.Text; - -public readonly partial struct Base32 -{ - /// - /// The Base-32 input data size. - /// - private const int InputSize = 8; - - /// - /// The Base-32 output data size. - /// - private const int OutputSize = 5; - - /// - /// Encode a byte array into a Base-32 string. - /// - /// The value to encode. - /// The Base-32 alphabet to use for encoding. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a Base-32 encoded string. - private static string Encode(byte[] value, string alphabet, bool padding) - { - if (value.Length == 0) - { - return string.Empty; - } - - StringBuilder builder = new(value.Length * InputSize / OutputSize); - - int inputPosition = 0; - int inputSubPosition = 0; - byte outputPosition = 0; - int outputSubPosition = 0; - - while (inputPosition < value.Length) - { - int availableBits = Math.Min(InputSize - inputSubPosition, OutputSize - outputSubPosition); - - outputPosition <<= availableBits; - outputPosition |= (byte) (value[inputPosition] >> (InputSize - (inputSubPosition + availableBits))); - inputSubPosition += availableBits; - - if (inputSubPosition >= InputSize) - { - inputPosition++; - inputSubPosition = 0; - } - - outputSubPosition += availableBits; - - if (outputSubPosition < OutputSize) continue; - - outputPosition &= 0x1F; - builder.Append(alphabet[outputPosition]); - outputSubPosition = 0; - } - - if (outputSubPosition <= 0) return builder.ToString(); - - outputPosition <<= (OutputSize - outputSubPosition); - outputPosition &= 0x1F; - builder.Append(alphabet[outputPosition]); - - while (padding && builder.Length % InputSize != 0) - { - builder.Append('='); - } - - return builder.ToString(); - } - - /// - /// Decodes a Base-32 into a byte array. - /// - /// The value to decode. - /// The Base-32 alphabet to use for decoding. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a byte array. - /// If the Base-32 string format is invalid. - private static byte[] Decode(ReadOnlySpan value, string alphabet, bool padding) - { - if (value == string.Empty) - { - return Array.Empty(); - } - - if (padding && value.Length % InputSize != 0) - { - throw new FormatException("Base32 string is invalid. Insufficient padding has been applied."); - } - - ReadOnlySpan valueWithoutPadding = padding ? value.TrimEnd('=') : value; - - byte[] outputBytes = new byte[valueWithoutPadding.Length * OutputSize / InputSize]; - - if (outputBytes.Length == 0) - { - throw new FormatException("Base32 string is invalid. Not enough data to construct byte array."); - } - - int inputPosition = 0; - int inputSubPosition = 0; - int outputPosition = 0; - int outputSubPosition = 0; - - while (outputPosition < outputBytes.Length) - { - char character = valueWithoutPadding[inputPosition]; - int index = alphabet.IndexOf(character); - - if (index < 0) - { - throw new FormatException($"Invalid Base32 character '{character}' at position {inputPosition}"); - } - - int availableBits = Math.Min(OutputSize - inputSubPosition, InputSize - outputSubPosition); - - outputBytes[outputPosition] <<= availableBits; - outputBytes[outputPosition] |= (byte) (index >> (OutputSize - (inputSubPosition + availableBits))); - outputSubPosition += availableBits; - - if (outputSubPosition >= InputSize) - { - outputPosition++; - outputSubPosition = 0; - } - - inputSubPosition += availableBits; - - if (inputSubPosition < OutputSize) continue; - - inputPosition++; - inputSubPosition = 0; - } - - return outputBytes; - } -} diff --git a/OnixLabs.Core/Text/Base32.Equatable.cs b/OnixLabs.Core/Text/Base32.Equatable.cs index ebdd552..e9f993e 100644 --- a/OnixLabs.Core/Text/Base32.Equatable.cs +++ b/OnixLabs.Core/Text/Base32.Equatable.cs @@ -1,73 +1,72 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Linq; using OnixLabs.Core.Linq; namespace OnixLabs.Core.Text; -public readonly partial struct Base32 : IEquatable +public readonly partial struct Base32 { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(Base32 a, Base32 b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(Base32 other) { - return Equals(a, b); + return other.value.SequenceEqual(value); } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(Base32 a, Base32 b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return obj is Base32 other && Equals(other); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public bool Equals(Base32 other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return other.Value.SequenceEqual(Value) && other.Alphabet == Alphabet && other.Padding == Padding; + return value.GetContentHashCode(); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(Base32 left, Base32 right) { - return obj is Base32 other && Equals(other); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(Base32 left, Base32 right) { - return HashCode.Combine(Value.GetContentHashCode()); + return !Equals(left, right); } } diff --git a/OnixLabs.Core/Text/Base32.Format.cs b/OnixLabs.Core/Text/Base32.Format.cs new file mode 100644 index 0000000..447bd83 --- /dev/null +++ b/OnixLabs.Core/Text/Base32.Format.cs @@ -0,0 +1,33 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +public readonly partial struct Base32 +{ + /// + /// Tries to format the value of the current instance into the provided span of characters. + /// + /// The span in which to write this instance's value formatted as a span of characters. + /// When this method returns, contains the number of characters that were written in . + /// A span containing the characters that represent a standard or custom format string that defines the acceptable format for . + /// An optional object that supplies culture-specific formatting information for . + /// Returns if the formatting was successful; otherwise, . + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return ToString(format, provider).TryCopyTo(destination, out charsWritten); + } +} diff --git a/OnixLabs.Core/Text/Base32.From.cs b/OnixLabs.Core/Text/Base32.From.cs deleted file mode 100644 index 0105180..0000000 --- a/OnixLabs.Core/Text/Base32.From.cs +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using System.Text; - -namespace OnixLabs.Core.Text; - -public readonly partial struct Base32 -{ - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Returns a new instance. - public static Base32 FromByteArray(byte[] value) - { - return FromByteArray(value, Base32Alphabet.Default); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromByteArray(byte[] value, Base32Alphabet alphabet) - { - return FromByteArray(value, alphabet, true); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromByteArray(byte[] value, bool padding) - { - return FromByteArray(value, Base32Alphabet.Default, padding); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromByteArray(byte[] value, Base32Alphabet alphabet, bool padding) - { - return new Base32(value, alphabet, padding); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Returns a new instance. - public static Base32 FromCharArray(char[] value) - { - return FromCharArray(value, Encoding.Default, Base32Alphabet.Default, true); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base32 FromCharArray(char[] value, Encoding encoding) - { - return FromCharArray(value, encoding, Base32Alphabet.Default, true); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromCharArray(char[] value, bool padding) - { - return FromCharArray(value, Encoding.Default, Base32Alphabet.Default, padding); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromCharArray(char[] value, Encoding encoding, bool padding) - { - return FromCharArray(value, encoding, Base32Alphabet.Default, padding); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromCharArray(char[] value, Base32Alphabet alphabet) - { - return FromCharArray(value, Encoding.Default, alphabet); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromCharArray(char[] value, Encoding encoding, Base32Alphabet alphabet) - { - byte[] bytes = encoding.GetBytes(value); - return FromByteArray(bytes, alphabet); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromCharArray(char[] value, Base32Alphabet alphabet, bool padding) - { - return FromCharArray(value, Encoding.Default, alphabet, padding); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromCharArray(char[] value, Encoding encoding, Base32Alphabet alphabet, bool padding) - { - byte[] bytes = encoding.GetBytes(value); - return FromByteArray(bytes, alphabet, padding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Returns a new instance. - public static Base32 FromSpan(ReadOnlySpan value) - { - return FromSpan(value, Encoding.Default, Base32Alphabet.Default, true); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base32 FromSpan(ReadOnlySpan value, Encoding encoding) - { - return FromSpan(value, encoding, Base32Alphabet.Default, true); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromSpan(ReadOnlySpan value, bool padding) - { - return FromSpan(value, Encoding.Default, Base32Alphabet.Default, padding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromSpan(ReadOnlySpan value, Encoding encoding, bool padding) - { - return FromSpan(value, encoding, Base32Alphabet.Default, padding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromSpan(ReadOnlySpan value, Base32Alphabet alphabet) - { - return FromSpan(value, Encoding.Default, alphabet); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromSpan(ReadOnlySpan value, Encoding encoding, Base32Alphabet alphabet) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding, alphabet); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromSpan(ReadOnlySpan value, Base32Alphabet alphabet, bool padding) - { - return FromSpan(value, Encoding.Default, alphabet, padding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromSpan(ReadOnlySpan value, Encoding encoding, Base32Alphabet alphabet, - bool padding) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding, alphabet, padding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Returns a new instance. - public static Base32 FromString(string value) - { - return FromString(value, Encoding.Default, Base32Alphabet.Default, true); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base32 FromString(string value, Encoding encoding) - { - return FromString(value, encoding, Base32Alphabet.Default, true); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromString(string value, bool padding) - { - return FromString(value, Encoding.Default, Base32Alphabet.Default, padding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromString(string value, Encoding encoding, bool padding) - { - return FromString(value, encoding, Base32Alphabet.Default, padding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromString(string value, Base32Alphabet alphabet) - { - return FromString(value, Encoding.Default, alphabet); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromString(string value, Encoding encoding, Base32Alphabet alphabet) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding, alphabet); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromString(string value, Base32Alphabet alphabet, bool padding) - { - return FromString(value, Encoding.Default, alphabet, padding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - /// Returns a new instance. - public static Base32 FromString(string value, Encoding encoding, Base32Alphabet alphabet, bool padding) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding, alphabet, padding); - } -} diff --git a/OnixLabs.Core/Text/Base32.Parse.cs b/OnixLabs.Core/Text/Base32.Parse.cs index 0b81a7a..a41403c 100644 --- a/OnixLabs.Core/Text/Base32.Parse.cs +++ b/OnixLabs.Core/Text/Base32.Parse.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,69 +19,62 @@ namespace OnixLabs.Core.Text; public readonly partial struct Base32 { /// - /// Parses a Base-32 value into a instance. + /// Parses the specified Base-32 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// A new instance. - public static Base32 Parse(string value) + /// The Base-32 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// Returns a new instance, parsed from the specified Base-32 encoded value. + public static Base32 Parse(string value, IFormatProvider? provider = null) { - return Parse(value, Base32Alphabet.Default); + return Parse(value.AsSpan(), provider); } /// - /// Parses a Base-32 value into a instance. + /// Parses the specified Base-32 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// A new instance. - public static Base32 Parse(string value, Base32Alphabet alphabet) + /// The Base-32 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// Returns a new instance, parsed from the specified Base-32 encoded value. + public static Base32 Parse(ReadOnlySpan value, IFormatProvider? provider = null) { - ReadOnlySpan characters = value.AsSpan(); - return Parse(characters, alphabet); + byte[] bytes = IBaseCodec.Base32.Decode(value, provider); + return new Base32(bytes); } /// - /// Parses a Base-32 value into a instance. + /// Tries to parse the specified Base-32 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// A new instance. - public static Base32 Parse(char[] value) + /// The Base-32 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// + /// A new instance, parsed from the specified Base-32 encoded value, + /// or the default value if the specified Base-32 encoded could not be parsed. + /// + /// Returns if the specified Base-32 value was decoded successfully; otherwise, . + public static bool TryParse(string? value, IFormatProvider? provider, out Base32 result) { - return Parse(value, Base32Alphabet.Default); + return TryParse(value.AsSpan(), provider, out result); } /// - /// Parses a Base-32 value into a instance. + /// Tries to parse the specified Base-32 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// A new instance. - public static Base32 Parse(char[] value, Base32Alphabet alphabet) + /// The Base-32 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// + /// A new instance, parsed from the specified Base-32 encoded value, + /// or the default value if the specified Base-32 encoded could not be parsed. + /// + /// Returns if the specified Base-32 value was decoded successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, IFormatProvider? provider, out Base32 result) { - ReadOnlySpan characters = value.AsSpan(); - return Parse(characters, alphabet); - } + if (IBaseCodec.Base32.TryDecode(value, provider, out byte[] bytes)) + { + result = new Base32(bytes); + return true; + } - /// - /// Parses a Base-32 value into a instance. - /// - /// The Base-16 (hexadecimal) value to parse. - /// A new instance. - public static Base32 Parse(ReadOnlySpan value) - { - return Parse(value, Base32Alphabet.Default); - } - - /// - /// Parses a Base-32 value into a instance. - /// - /// The Base-16 (hexadecimal) value to parse. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// A new instance. - public static Base32 Parse(ReadOnlySpan value, Base32Alphabet alphabet) - { - bool padding = value.Contains('='); - byte[] bytes = Decode(value, alphabet.Alphabet, padding); - return FromByteArray(bytes, padding); + result = default; + return false; } } diff --git a/OnixLabs.Core/Text/Base32.To.cs b/OnixLabs.Core/Text/Base32.To.cs index eded14e..0b38cc9 100644 --- a/OnixLabs.Core/Text/Base32.To.cs +++ b/OnixLabs.Core/Text/Base32.To.cs @@ -1,57 +1,70 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Text; +using System; namespace OnixLabs.Core.Text; public readonly partial struct Base32 { /// - /// Returns a array that represents the current object. + /// Gets the underlying representation of the current instance. /// - /// Returns a array that represents the current object. + /// Return the underlying representation of the current instance. public byte[] ToByteArray() { - return Value.Copy(); + return value.Copy(); } /// - /// Returns a that represents the current object in plain text. + /// Returns a that represents the current object. /// - /// Returns a that represents the current object in plain text. - public string ToPlainTextString() + /// Returns a that represents the current object. + public override string ToString() { - return ToPlainTextString(Encoding.Default); + return ToString(Base32FormatProvider.Rfc4648); } /// - /// Returns a that represents the current object in plain text. + /// Formats the value of the current instance using the specified format. /// - /// The encoding to use to obtain the underlying value. - /// Returns a that represents the current object in plain text. - public string ToPlainTextString(Encoding encoding) + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(IFormatProvider? formatProvider) { - return encoding.GetString(Value); + return ToString(null, formatProvider); } /// - /// Returns a that represents the current object. + /// Formats the value of the current instance using the specified format. /// - /// A that represents the current object. - public override string ToString() + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(format.AsSpan(), formatProvider); + } + + /// + /// Formats the value of the current instance using the specified format. + /// + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(ReadOnlySpan format, IFormatProvider? formatProvider) { - return Encode(Value, Alphabet.Alphabet, Padding); + return IBaseCodec.Base32.Encode(value, formatProvider); } } diff --git a/OnixLabs.Core/Text/Base32.cs b/OnixLabs.Core/Text/Base32.cs index 855b754..d19042d 100644 --- a/OnixLabs.Core/Text/Base32.cs +++ b/OnixLabs.Core/Text/Base32.cs @@ -1,49 +1,33 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Core.Text; /// /// Represents a Base-32 value. /// -public readonly partial struct Base32 +/// The underlying value. +public readonly partial struct Base32(ReadOnlySpan value) : IBaseValue { + private readonly byte[] value = value.ToArray(); + /// - /// Initializes a new instance of the struct. + /// Initializes a new default value. /// - /// The underlying value. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - /// Determines whether padding should be applied for Base-32 encoding and decoding operations. - private Base32(byte[] value, Base32Alphabet alphabet, bool padding) + public Base32() : this([]) { - Value = value; - Alphabet = alphabet; - Padding = padding; } - - /// - /// Gets the underlying value. - /// - private byte[] Value { get; } - - /// - /// Gets the alphabet that will be used for Base-32 encoding and decoding operations. - /// - private Base32Alphabet Alphabet { get; } - - /// - /// Gets a value that determines whether padding should be applied for Base-32 encoding and decoding operations. - /// - private bool Padding { get; } } diff --git a/OnixLabs.Core/Text/Base32Alphabet.cs b/OnixLabs.Core/Text/Base32Alphabet.cs deleted file mode 100644 index b27ca52..0000000 --- a/OnixLabs.Core/Text/Base32Alphabet.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Core.Text; - -/// -/// Specifies the supported Base-32 alphabets. -/// -public sealed class Base32Alphabet : Enumeration -{ - /// - /// The default (RFC-4648) Base-32 alphabet. - /// - public static readonly Base32Alphabet Default = - new(0, nameof(Default), "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"); - - /// - /// The Z-Base-32 alphabet. - /// - public static readonly Base32Alphabet ZBase32 = - new(1, nameof(ZBase32), "ybndrfg8ejkmcpqxot1uwisza345h769"); - - /// - /// The Geohash Base-32 alphabet. - /// - public static readonly Base32Alphabet GeoHash = - new(2, nameof(GeoHash), "0123456789bcdefghjkmnpqrstuvwxyz"); - - /// - /// The Crockford Base-32 alphabet. - /// - public static readonly Base32Alphabet Crockford = - new(3, nameof(Crockford), "0123456789ABCDEFGHJKMNPQRSTVWXYZ"); - - /// - /// The Base-32 Hex alphabet. - /// - public static readonly Base32Alphabet Base32Hex = - new(4, nameof(Base32Hex), "0123456789ABCDEFGHIJKLMNOPQRSTUV"); - - /// - /// Initializes a new instance of the class. - /// - /// The value of the enumeration entry. - /// The name of the enumeration entry. - /// The alphabet that will be used for Base-32 encoding and decoding operations. - private Base32Alphabet(int value, string name, string alphabet) : base(value, name) - { - Alphabet = alphabet; - } - - /// - /// Gets the alphabet that will be used for Base-32 encoding and decoding operations. - /// - public string Alphabet { get; } -} diff --git a/OnixLabs.Core/Text/Base32Codec.cs b/OnixLabs.Core/Text/Base32Codec.cs new file mode 100644 index 0000000..1db0cbd --- /dev/null +++ b/OnixLabs.Core/Text/Base32Codec.cs @@ -0,0 +1,227 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Text; + +namespace OnixLabs.Core.Text; + +/// +/// Represents a codec for encoding and decoding Base-32 values. +/// +public sealed class Base32Codec : IBaseCodec +{ + /// + /// The Base-32 input data size. + /// + private const int InputSize = 8; + + /// + /// The Base-32 output data size. + /// + private const int OutputSize = 5; + + /// + /// Encodes the specified value into a Base-32 representation. + /// + /// The value to encode into a Base-32 representation. + /// The format provider that will be used to encode the specified value. + /// Returns a new Base-32 representation encoded from the specified value. + public string Encode(ReadOnlySpan value, IFormatProvider? provider = null) + { + if (TryEncode(value, provider, out string result)) return result; + throw new FormatException(IBaseCodec.EncodingFormatException); + } + + /// + /// Decodes the specified Base-32 representation into a . + /// + /// The Base-32 value to decode into a . + /// The format provider that will be used to decode the specified value. + /// Returns a new decoded from the specified value. + public byte[] Decode(ReadOnlySpan value, IFormatProvider? provider = null) + { + if (TryDecode(value, provider, out byte[] result)) return result; + throw new FormatException(IBaseCodec.DecodingFormatException); + } + + /// + /// Tries to encode the specified value into a Base-32 representation. + /// + /// The value to encode into a Base-32 representation. + /// The format provider that will be used to encode the specified value. + /// + /// A new Base-32 representation encoded from the specified value, + /// or an empty string if the specified value could not be encoded. + /// + /// Returns if the specified value was encoded successfully; otherwise, . + public bool TryEncode(ReadOnlySpan value, IFormatProvider? provider, out string result) + { + try + { + if (value.IsEmpty) + { + result = string.Empty; + return true; + } + + if (provider is not null && provider is not Base32FormatProvider) + { + result = string.Empty; + return false; + } + + Base32FormatProvider formatProvider = provider as Base32FormatProvider ?? Base32FormatProvider.Rfc4648; + StringBuilder builder = new(value.Length * InputSize / OutputSize); + + int inputPosition = 0; + int inputSubPosition = 0; + byte outputPosition = 0; + int outputSubPosition = 0; + + while (inputPosition < value.Length) + { + int availableBits = Math.Min(InputSize - inputSubPosition, OutputSize - outputSubPosition); + + outputPosition <<= availableBits; + outputPosition |= (byte)(value[inputPosition] >> (InputSize - (inputSubPosition + availableBits))); + inputSubPosition += availableBits; + + if (inputSubPosition >= InputSize) + { + inputPosition++; + inputSubPosition = 0; + } + + outputSubPosition += availableBits; + + if (outputSubPosition < OutputSize) continue; + + outputPosition &= 0x1F; + builder.Append(formatProvider.Alphabet[outputPosition]); + outputSubPosition = 0; + } + + if (outputSubPosition <= 0) + { + result = builder.ToString(); + return true; + } + + outputPosition <<= (OutputSize - outputSubPosition); + outputPosition &= 0x1F; + builder.Append(formatProvider.Alphabet[outputPosition]); + + while (formatProvider.IsPadded && builder.Length % InputSize != 0) builder.Append('='); + + result = builder.ToString(); + return true; + } + catch + { + result = string.Empty; + return false; + } + } + + /// + /// Tries to decode the specified Base-32 representation into a . + /// + /// The Base-32 value to decode into a . + /// The format provider that will be used to decode the specified value. + /// + /// A new decoded from the specified value, + /// or an empty if the specified value could not be decoded. + /// + /// Returns if the specified value was decoded successfully; otherwise, . + public bool TryDecode(ReadOnlySpan value, IFormatProvider? provider, out byte[] result) + { + try + { + if (value.IsEmpty) + { + result = []; + return true; + } + + if (provider is not null && provider is not Base32FormatProvider) + { + result = []; + return false; + } + + Base32FormatProvider formatProvider = provider as Base32FormatProvider ?? Base32FormatProvider.Rfc4648; + + if (formatProvider.IsPadded && value.Length % InputSize != 0) + { + result = []; + return false; + } + + ReadOnlySpan valueWithoutPadding = formatProvider.IsPadded ? value.TrimEnd('=') : value; + + byte[] outputBytes = new byte[valueWithoutPadding.Length * OutputSize / InputSize]; + + if (outputBytes.Length == 0) + { + result = []; + return false; + } + + int inputPosition = 0; + int inputSubPosition = 0; + int outputPosition = 0; + int outputSubPosition = 0; + + while (outputPosition < outputBytes.Length) + { + char character = valueWithoutPadding[inputPosition]; + int index = formatProvider.Alphabet.IndexOf(character); + + if (index < 0) + { + result = []; + return false; + } + + int availableBits = Math.Min(OutputSize - inputSubPosition, InputSize - outputSubPosition); + + outputBytes[outputPosition] <<= availableBits; + outputBytes[outputPosition] |= (byte)(index >> (OutputSize - (inputSubPosition + availableBits))); + outputSubPosition += availableBits; + + if (outputSubPosition >= InputSize) + { + outputPosition++; + outputSubPosition = 0; + } + + inputSubPosition += availableBits; + + if (inputSubPosition < OutputSize) continue; + + inputPosition++; + inputSubPosition = 0; + } + + result = outputBytes; + return true; + } + catch + { + result = []; + return false; + } + } +} diff --git a/OnixLabs.Core/Text/Base32FormatProvider.cs b/OnixLabs.Core/Text/Base32FormatProvider.cs new file mode 100644 index 0000000..28c0d72 --- /dev/null +++ b/OnixLabs.Core/Text/Base32FormatProvider.cs @@ -0,0 +1,107 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +/// +/// Represents a Base-32 format provider. +/// +public sealed class Base32FormatProvider : Enumeration, IFormatProvider +{ + /// + /// Gets the RFC 4648 Base-32 format provider. + /// + public static readonly Base32FormatProvider Rfc4648 = new(0, nameof(Rfc4648), "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", false); + + /// + /// Gets the Z-Base-32 format provider. + /// + public static readonly Base32FormatProvider ZBase32 = new(1, nameof(ZBase32), "ybndrfg8ejkmcpqxot1uwisza345h769", false); + + /// + /// Gets the GeoHash Base-32 format provider. + /// + public static readonly Base32FormatProvider GeoHash = new(2, nameof(GeoHash), "0123456789bcdefghjkmnpqrstuvwxyz", false); + + /// + /// Gets the Crockford Base-32 format provider. + /// + public static readonly Base32FormatProvider Crockford = new(3, nameof(Crockford), "0123456789ABCDEFGHJKMNPQRSTVWXYZ", false); + + /// + /// Gets the Base-32 Hex format provider. + /// + public static readonly Base32FormatProvider Base32Hex = new(4, nameof(Base32Hex), "0123456789ABCDEFGHIJKLMNOPQRSTUV", false); + + /// + /// Gets the RFC 4648 Base-32 format provider. + /// + public static readonly Base32FormatProvider PaddedRfc4648 = new(5, nameof(PaddedRfc4648), "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", true); + + /// + /// Gets the Z-Base-32 format provider. + /// + public static readonly Base32FormatProvider PaddedZBase32 = new(6, nameof(PaddedZBase32), "ybndrfg8ejkmcpqxot1uwisza345h769", true); + + /// + /// Gets the GeoHash Base-32 format provider. + /// + public static readonly Base32FormatProvider PaddedGeoHash = new(7, nameof(PaddedGeoHash), "0123456789bcdefghjkmnpqrstuvwxyz", true); + + /// + /// Gets the Crockford Base-32 format provider. + /// + public static readonly Base32FormatProvider PaddedCrockford = new(8, nameof(PaddedCrockford), "0123456789ABCDEFGHJKMNPQRSTVWXYZ", true); + + /// + /// Gets the Base-32 Hex format provider. + /// + public static readonly Base32FormatProvider PaddedBase32Hex = new(9, nameof(PaddedBase32Hex), "0123456789ABCDEFGHIJKLMNOPQRSTUV", true); + + /// + /// Initializes a new instance of the class. + /// + /// The value of the enumeration entry. + /// The name of the enumeration entry. + /// The alphabet of the format provider. + /// A value indicating whether the format provider uses padding. + private Base32FormatProvider(int value, string name, string alphabet, bool isPadded) : base(value, name) + { + Alphabet = alphabet; + IsPadded = isPadded; + } + + /// + /// Gets the alphabet of the current instance. + /// + public string Alphabet { get; } + + /// + /// Gets a value indicating whether the current instance uses padding. + /// + public bool IsPadded { get; } + + /// Gets an object that provides formatting services for the specified type. + /// An object that specifies the type of format object to return. + /// + /// Returns an instance of the object specified by , + /// if the implementation can supply that type of object; otherwise, . + /// + public object? GetFormat(Type? formatType) + { + return formatType == typeof(Base32FormatProvider) ? this : null; + } +} diff --git a/OnixLabs.Core/Text/Base58.Checksum.cs b/OnixLabs.Core/Text/Base58.Checksum.cs deleted file mode 100644 index 9222d17..0000000 --- a/OnixLabs.Core/Text/Base58.Checksum.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using System.Security.Cryptography; - -namespace OnixLabs.Core.Text; - -public readonly partial struct Base58 -{ - /// - /// The size of a Base-58 checksum. - /// - private const int ChecksumSize = 4; - - /// - /// Computes a Base-58 checksum. - /// - /// The value for which to compute a Base-58 checksum. - /// Returns the computed Base-58 checksum. - private static byte[] ComputeChecksum(byte[] value) - { - using HashAlgorithm algorithm = SHA256.Create(); - - byte[] hashedValue = algorithm.ComputeHash(value); - byte[] checksum = algorithm.ComputeHash(hashedValue); - byte[] result = new byte[ChecksumSize]; - - Buffer.BlockCopy(checksum, 0, result, 0, result.Length); - - return result; - } - - /// - /// Adds a checksum to the specified byte array. - /// - /// The value for which to add a checksum. - /// Returns the original value with a checksum. - private static byte[] AddChecksum(byte[] value) - { - byte[] checksum = ComputeChecksum(value); - return value.ConcatenateWith(checksum); - } - - /// - /// Removes a checksum from the specified byte array. - /// - /// The value for which to remove a checksum. - /// Returns the original value without a checksum. - private static byte[] RemoveChecksum(byte[] value) - { - return value.Copy(0, value.Length - ChecksumSize); - } - - /// - /// Gets a checksum from the specified byte array. - /// - /// The value from which to obtain a checksum. - /// Returns a checksum from the specified byte array. - private static byte[] GetChecksum(byte[] value) - { - return value.Copy(value.Length - ChecksumSize - 1, ChecksumSize); - } - - /// - /// Verifies a Base-58 checksum. - /// - /// The value for which to verify its checksum. - /// If the Base-58 checksum is invalid. - private static void VerifyChecksum(byte[] value) - { - byte[] valueWithoutChecksum = RemoveChecksum(value); - byte[] originalChecksum = GetChecksum(value); - byte[] computedChecksum = ComputeChecksum(valueWithoutChecksum); - - if (!originalChecksum.SequenceEqual(computedChecksum)) - { - throw new FormatException("Base-58 checksum is invalid."); - } - } -} diff --git a/OnixLabs.Core/Text/Base58.Codec.cs b/OnixLabs.Core/Text/Base58.Codec.cs deleted file mode 100644 index 36b1146..0000000 --- a/OnixLabs.Core/Text/Base58.Codec.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; - -namespace OnixLabs.Core.Text; - -public readonly partial struct Base58 -{ - /// - /// Encode a byte array into a Base-58 string. - /// - /// The value to encode. - /// The Base-58 alphabet to use for encoding. - /// Returns a Base-58 encoded string. - private static string Encode(byte[] value, string alphabet) - { - BigInteger data = value.Aggregate(BigInteger.Zero, (a, b) => a * 256 + b); - StringBuilder result = new(); - - while (data > 0) - { - BigInteger remainder = data % 58; - data /= 58; - result.Insert(0, alphabet[(int) remainder]); - } - - for (int index = 0; index < value.Length && value[index] == 0; index++) - { - result.Insert(0, '1'); - } - - return result.ToString(); - } - - /// - /// Decodes a Base-58 into a byte array. - /// - /// The value to decode. - /// The Base-58 alphabet to use for decoding. - /// Returns a byte array. - /// If the Base-58 string format is invalid. - private static byte[] Decode(ReadOnlySpan value, string alphabet) - { - BigInteger data = BigInteger.Zero; - - for (int index = 0; index < value.Length; index++) - { - char character = value[index]; - int characterIndex = alphabet.IndexOf(character); - - if (characterIndex < 0) - { - throw new FormatException($"Invalid Base58 character '{character}' at position {index}"); - } - - data = data * 58 + characterIndex; - } - - int leadingZeroCount = value - .ToArray() - .TakeWhile(character => character == '1') - .Count(); - - IEnumerable leadingZeros = Enumerable - .Repeat(byte.MinValue, leadingZeroCount); - - IEnumerable bytesWithoutLeadingZeros = data - .ToByteArray() - .Reverse() - .SkipWhile(byteValue => byteValue == 0); - - return leadingZeros - .Concat(bytesWithoutLeadingZeros) - .ToArray(); - } -} diff --git a/OnixLabs.Core/Text/Base58.Equatable.cs b/OnixLabs.Core/Text/Base58.Equatable.cs index d310372..40020c3 100644 --- a/OnixLabs.Core/Text/Base58.Equatable.cs +++ b/OnixLabs.Core/Text/Base58.Equatable.cs @@ -1,73 +1,72 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Linq; using OnixLabs.Core.Linq; namespace OnixLabs.Core.Text; -public readonly partial struct Base58 : IEquatable +public readonly partial struct Base58 { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(Base58 a, Base58 b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(Base58 other) { - return Equals(a, b); + return other.value.SequenceEqual(value); } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(Base58 a, Base58 b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return obj is Base58 other && Equals(other); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public bool Equals(Base58 other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return other.Value.SequenceEqual(Value) && other.Alphabet == Alphabet; + return value.GetContentHashCode(); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(Base58 left, Base58 right) { - return obj is Base58 other && Equals(other); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(Base58 left, Base58 right) { - return HashCode.Combine(Value.GetContentHashCode()); + return !Equals(left, right); } } diff --git a/OnixLabs.Core/Text/Base58.Format.cs b/OnixLabs.Core/Text/Base58.Format.cs new file mode 100644 index 0000000..7f63a64 --- /dev/null +++ b/OnixLabs.Core/Text/Base58.Format.cs @@ -0,0 +1,33 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +public readonly partial struct Base58 +{ + /// + /// Tries to format the value of the current instance into the provided span of characters. + /// + /// The span in which to write this instance's value formatted as a span of characters. + /// When this method returns, contains the number of characters that were written in . + /// A span containing the characters that represent a standard or custom format string that defines the acceptable format for . + /// An optional object that supplies culture-specific formatting information for . + /// Returns if the formatting was successful; otherwise, . + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return ToString(format, provider).TryCopyTo(destination, out charsWritten); + } +} diff --git a/OnixLabs.Core/Text/Base58.From.cs b/OnixLabs.Core/Text/Base58.From.cs deleted file mode 100644 index 807c59f..0000000 --- a/OnixLabs.Core/Text/Base58.From.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using System.Text; - -namespace OnixLabs.Core.Text; - -public readonly partial struct Base58 -{ - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Returns a new instance. - public static Base58 FromByteArray(byte[] value) - { - return new Base58(value, Base58Alphabet.Default); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// Returns a new instance. - public static Base58 FromByteArray(byte[] value, Base58Alphabet alphabet) - { - return new Base58(value, alphabet); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Returns a new instance. - public static Base58 FromCharArray(char[] value) - { - return FromCharArray(value, Encoding.Default, Base58Alphabet.Default); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base58 FromCharArray(char[] value, Encoding encoding) - { - return FromCharArray(value, encoding, Base58Alphabet.Default); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// Returns a new instance. - public static Base58 FromCharArray(char[] value, Base58Alphabet alphabet) - { - return FromCharArray(value, Encoding.Default, alphabet); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// Returns a new instance. - public static Base58 FromCharArray(char[] value, Encoding encoding, Base58Alphabet alphabet) - { - byte[] bytes = encoding.GetBytes(value); - return FromByteArray(bytes, alphabet); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Returns a new instance. - public static Base58 FromSpan(ReadOnlySpan value) - { - return FromSpan(value, Encoding.Default, Base58Alphabet.Default); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base58 FromSpan(ReadOnlySpan value, Encoding encoding) - { - return FromSpan(value, encoding, Base58Alphabet.Default); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// Returns a new instance. - public static Base58 FromSpan(ReadOnlySpan value, Base58Alphabet alphabet) - { - return FromSpan(value, Encoding.Default, alphabet); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// Returns a new instance. - public static Base58 FromSpan(ReadOnlySpan value, Encoding encoding, Base58Alphabet alphabet) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding, alphabet); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Returns a new instance. - public static Base58 FromString(string value) - { - return FromString(value, Encoding.Default, Base58Alphabet.Default); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base58 FromString(string value, Encoding encoding) - { - return FromString(value, encoding, Base58Alphabet.Default); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// Returns a new instance. - public static Base58 FromString(string value, Base58Alphabet alphabet) - { - return FromString(value, Encoding.Default, alphabet); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// Returns a new instance. - public static Base58 FromString(string value, Encoding encoding, Base58Alphabet alphabet) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding, alphabet); - } -} diff --git a/OnixLabs.Core/Text/Base58.Parse.cs b/OnixLabs.Core/Text/Base58.Parse.cs index ef75b70..a09da23 100644 --- a/OnixLabs.Core/Text/Base58.Parse.cs +++ b/OnixLabs.Core/Text/Base58.Parse.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,138 +19,62 @@ namespace OnixLabs.Core.Text; public readonly partial struct Base58 { /// - /// Parses a Base-58 value into a instance. + /// Parses the specified Base-58 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// A new instance. - public static Base58 Parse(string value) + /// The Base-58 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// Returns a new instance, parsed from the specified Base-58 encoded value. + public static Base58 Parse(string value, IFormatProvider? provider = null) { - return Parse(value, Base58Alphabet.Default); + return Parse(value.AsSpan(), provider); } /// - /// Parses a Base-58 value into a instance. + /// Parses the specified Base-58 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// A new instance. - public static Base58 Parse(string value, Base58Alphabet alphabet) + /// The Base-58 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// Returns a new instance, parsed from the specified Base-58 encoded value. + public static Base58 Parse(ReadOnlySpan value, IFormatProvider? provider = null) { - ReadOnlySpan characters = value.AsSpan(); - return Parse(characters, alphabet); + byte[] bytes = IBaseCodec.Base58.Decode(value, provider); + return new Base58(bytes); } /// - /// Parses a Base-58 value into a instance. + /// Tries to parse the specified Base-58 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// A new instance. - public static Base58 Parse(char[] value) + /// The Base-58 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// + /// A new instance, parsed from the specified Base-58 encoded value, + /// or the default value if the specified Base-58 encoded could not be parsed. + /// + /// Returns if the specified Base-58 value was decoded successfully; otherwise, . + public static bool TryParse(string? value, IFormatProvider? provider, out Base58 result) { - return Parse(value, Base58Alphabet.Default); + return TryParse(value.AsSpan(), provider, out result); } /// - /// Parses a Base-58 value into a instance. + /// Tries to parse the specified Base-58 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// A new instance. - public static Base58 Parse(char[] value, Base58Alphabet alphabet) + /// The Base-58 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// + /// A new instance, parsed from the specified Base-58 encoded value, + /// or the default value if the specified Base-58 encoded could not be parsed. + /// + /// Returns if the specified Base-58 value was decoded successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, IFormatProvider? provider, out Base58 result) { - ReadOnlySpan characters = value.AsSpan(); - return Parse(characters, alphabet); - } - - /// - /// Parses a Base-58 value into a instance. - /// - /// The Base-16 (hexadecimal) value to parse. - /// A new instance. - public static Base58 Parse(ReadOnlySpan value) - { - return Parse(value, Base58Alphabet.Default); - } - - /// - /// Parses a Base-58 value into a instance. - /// - /// The Base-16 (hexadecimal) value to parse. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// A new instance. - public static Base58 Parse(ReadOnlySpan value, Base58Alphabet alphabet) - { - byte[] bytes = Decode(value, alphabet.Alphabet); - return FromByteArray(bytes, alphabet); - } - - /// - /// Parses a Base-58 value with a checksum into a instance. - /// - /// The Base-16 (hexadecimal) value to ParseWithChecksum. - /// A new instance. - public static Base58 ParseWithChecksum(string value) - { - return ParseWithChecksum(value, Base58Alphabet.Default); - } - - /// - /// Parses a Base-58 value with a checksum into a instance. - /// - /// The Base-16 (hexadecimal) value to ParseWithChecksum. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// A new instance. - public static Base58 ParseWithChecksum(string value, Base58Alphabet alphabet) - { - ReadOnlySpan characters = value.AsSpan(); - return ParseWithChecksum(characters, alphabet); - } - - /// - /// Parses a Base-58 value with a checksum into a instance. - /// - /// The Base-16 (hexadecimal) value to ParseWithChecksum. - /// A new instance. - public static Base58 ParseWithChecksum(char[] value) - { - return ParseWithChecksum(value, Base58Alphabet.Default); - } - - /// - /// Parses a Base-58 value with a checksum into a instance. - /// - /// The Base-16 (hexadecimal) value to ParseWithChecksum. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// A new instance. - public static Base58 ParseWithChecksum(char[] value, Base58Alphabet alphabet) - { - ReadOnlySpan characters = value.AsSpan(); - return ParseWithChecksum(characters, alphabet); - } - - /// - /// Parses a Base-58 value with a checksum into a instance. - /// - /// The Base-16 (hexadecimal) value to ParseWithChecksum. - /// A new instance. - public static Base58 ParseWithChecksum(ReadOnlySpan value) - { - return ParseWithChecksum(value, Base58Alphabet.Default); - } - - /// - /// Parses a Base-58 value with a checksum into a instance. - /// - /// The Base-16 (hexadecimal) value to ParseWithChecksum. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - /// A new instance. - public static Base58 ParseWithChecksum(ReadOnlySpan value, Base58Alphabet alphabet) - { - byte[] bytes = Decode(value, alphabet.Alphabet); - byte[] bytesWithoutChecksum = RemoveChecksum(bytes); - - VerifyChecksum(bytes); - - return FromByteArray(bytesWithoutChecksum, alphabet); + if (IBaseCodec.Base58.TryDecode(value, provider, out byte[] bytes)) + { + result = new Base58(bytes); + return true; + } + + result = default; + return false; } } diff --git a/OnixLabs.Core/Text/Base58.To.cs b/OnixLabs.Core/Text/Base58.To.cs index 767e2f8..d3f2ae9 100644 --- a/OnixLabs.Core/Text/Base58.To.cs +++ b/OnixLabs.Core/Text/Base58.To.cs @@ -1,68 +1,70 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Text; +using System; namespace OnixLabs.Core.Text; public readonly partial struct Base58 { /// - /// Returns a array that represents the current object. + /// Gets the underlying representation of the current instance. /// - /// Returns a array that represents the current object. + /// Return the underlying representation of the current instance. public byte[] ToByteArray() { - return Value.Copy(); + return value.Copy(); } /// - /// Returns a that represents the current object, with a checksum. + /// Returns a that represents the current object. /// - /// A that represents the current object, with a checksum. - public string ToStringWithChecksum() + /// Returns a that represents the current object. + public override string ToString() { - byte[] valueWithChecksum = AddChecksum(Value); - return Encode(valueWithChecksum, Alphabet.Alphabet); + return ToString(Base58FormatProvider.Bitcoin); } /// - /// Returns a that represents the current object in plain text. + /// Formats the value of the current instance using the specified format. /// - /// Returns a that represents the current object in plain text. - public string ToPlainTextString() + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(IFormatProvider? formatProvider) { - return ToPlainTextString(Encoding.Default); + return ToString(null, formatProvider); } /// - /// Returns a that represents the current object in plain text. + /// Formats the value of the current instance using the specified format. /// - /// The encoding to use to obtain the underlying value. - /// Returns a that represents the current object in plain text. - public string ToPlainTextString(Encoding encoding) + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(string? format, IFormatProvider? formatProvider) { - return encoding.GetString(Value); + return ToString(format.AsSpan(), formatProvider); } - /// - /// Returns a that represents the current object. + /// Formats the value of the current instance using the specified format. /// - /// A that represents the current object. - public override string ToString() + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(ReadOnlySpan format, IFormatProvider? formatProvider) { - return Encode(Value, Alphabet.Alphabet); + return IBaseCodec.Base58.Encode(value, formatProvider); } } diff --git a/OnixLabs.Core/Text/Base58.cs b/OnixLabs.Core/Text/Base58.cs index 178bcae..3bbfc8a 100644 --- a/OnixLabs.Core/Text/Base58.cs +++ b/OnixLabs.Core/Text/Base58.cs @@ -1,42 +1,33 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Core.Text; /// /// Represents a Base-58 value. /// -public readonly partial struct Base58 +/// The underlying value. +public readonly partial struct Base58(ReadOnlySpan value) : IBaseValue { + private readonly byte[] value = value.ToArray(); + /// - /// Initializes a new instance of the struct. + /// Initializes a new default value. /// - /// The underlying value. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - private Base58(byte[] value, Base58Alphabet alphabet) + public Base58() : this([]) { - Value = value; - Alphabet = alphabet; } - - /// - /// Gets the underlying value. - /// - private byte[] Value { get; } - - /// - /// Gets the alphabet that will be used for Base-58 encoding and decoding operations. - /// - private Base58Alphabet Alphabet { get; } } diff --git a/OnixLabs.Core/Text/Base58Alphabet.cs b/OnixLabs.Core/Text/Base58Alphabet.cs deleted file mode 100644 index 6cde86c..0000000 --- a/OnixLabs.Core/Text/Base58Alphabet.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Core.Text; - -/// -/// Specifies the supported Base-58 alphabets. -/// -public sealed class Base58Alphabet : Enumeration -{ - /// - /// The default Base-58 alphabet, which is the same as Bitcoin's Base-58 alphabet. - /// - public static readonly Base58Alphabet Default = - new(0, nameof(Default), "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); - - /// - /// The Ripple Base-58 alphabet. - /// - public static readonly Base58Alphabet Ripple = - new(0, nameof(Ripple), "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"); - - /// - /// The Flickr Base-58 alphabet. - /// - public static readonly Base58Alphabet Flickr = - new(0, nameof(Flickr), "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"); - - /// - /// Initializes a new instance of the class. - /// - /// The value of the enumeration entry. - /// The name of the enumeration entry. - /// The alphabet that will be used for Base-58 encoding and decoding operations. - private Base58Alphabet(int value, string name, string alphabet) : base(value, name) - { - Alphabet = alphabet; - } - - /// - /// Gets the alphabet that will be used for Base-58 encoding and decoding operations. - /// - public string Alphabet { get; } -} diff --git a/OnixLabs.Core/Text/Base58Codec.cs b/OnixLabs.Core/Text/Base58Codec.cs new file mode 100644 index 0000000..8b4c15e --- /dev/null +++ b/OnixLabs.Core/Text/Base58Codec.cs @@ -0,0 +1,170 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; + +namespace OnixLabs.Core.Text; + +/// +/// Represents a codec for encoding and decoding Base-58 values. +/// +public sealed class Base58Codec : IBaseCodec +{ + /// + /// Encodes the specified value into a Base-58 representation. + /// + /// The value to encode into a Base-58 representation. + /// The format provider that will be used to encode the specified value. + /// Returns a new Base-58 representation encoded from the specified value. + public string Encode(ReadOnlySpan value, IFormatProvider? provider = null) + { + if (TryEncode(value, provider, out string result)) return result; + throw new FormatException(IBaseCodec.EncodingFormatException); + } + + /// + /// Decodes the specified Base-58 representation into a . + /// + /// The Base-58 value to decode into a . + /// The format provider that will be used to decode the specified value. + /// Returns a new decoded from the specified value. + public byte[] Decode(ReadOnlySpan value, IFormatProvider? provider = null) + { + if (TryDecode(value, provider, out byte[] result)) return result; + throw new FormatException(IBaseCodec.DecodingFormatException); + } + + /// + /// Tries to encode the specified value into a Base-58 representation. + /// + /// The value to encode into a Base-58 representation. + /// The format provider that will be used to encode the specified value. + /// + /// A new Base-58 representation encoded from the specified value, + /// or an empty string if the specified value could not be encoded. + /// + /// Returns if the specified value was encoded successfully; otherwise, . + public bool TryEncode(ReadOnlySpan value, IFormatProvider? provider, out string result) + { + try + { + if (value.IsEmpty) + { + result = string.Empty; + return true; + } + + if (provider is not null && provider is not Base58FormatProvider) + { + result = string.Empty; + return false; + } + + Base58FormatProvider formatProvider = provider as Base58FormatProvider ?? Base58FormatProvider.Bitcoin; + StringBuilder builder = new(); + BigInteger data = BigInteger.Zero; + foreach (byte b in value) data = data * 256 + b; + + while (data > 0) + { + BigInteger remainder = data % 58; + data /= 58; + builder.Insert(0, formatProvider.Alphabet[(int)remainder]); + } + + for (int index = 0; index < value.Length && value[index] == 0; index++) builder.Insert(0, '1'); + + result = builder.ToString(); + return true; + } + catch + { + result = string.Empty; + return false; + } + } + + /// + /// Tries to decode the specified Base-58 representation into a . + /// + /// The Base-58 value to decode into a . + /// The format provider that will be used to decode the specified value. + /// + /// A new decoded from the specified value, + /// or an empty if the specified value could not be decoded. + /// + /// Returns if the specified value was decoded successfully; otherwise, . + public bool TryDecode(ReadOnlySpan value, IFormatProvider? provider, out byte[] result) + { + try + { + if (value.IsEmpty) + { + result = []; + return true; + } + + if (provider is not null && provider is not Base58FormatProvider) + { + result = []; + return false; + } + + Base58FormatProvider formatProvider = provider as Base58FormatProvider ?? Base58FormatProvider.Bitcoin; + + BigInteger data = BigInteger.Zero; + + foreach (char character in value) + { + int characterIndex = formatProvider.Alphabet.IndexOf(character); + + if (characterIndex < 0) + { + result = []; + return false; + } + + data = data * 58 + characterIndex; + } + + int leadingZeroCount = value + .ToArray() + .TakeWhile(character => character == '1') + .Count(); + + IEnumerable leadingZeros = Enumerable + .Repeat(byte.MinValue, leadingZeroCount); + + IEnumerable bytesWithoutLeadingZeros = data + .ToByteArray() + .Reverse() + .SkipWhile(byteValue => byteValue == 0); + + result = leadingZeros + .Concat(bytesWithoutLeadingZeros) + .ToArray(); + + return true; + } + catch + { + result = []; + return false; + } + } +} diff --git a/OnixLabs.Core/Text/Base58FormatProvider.cs b/OnixLabs.Core/Text/Base58FormatProvider.cs new file mode 100644 index 0000000..360e9d2 --- /dev/null +++ b/OnixLabs.Core/Text/Base58FormatProvider.cs @@ -0,0 +1,66 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +/// +/// Represents a Base-58 format provider. +/// +public sealed class Base58FormatProvider : Enumeration, IFormatProvider +{ + /// + /// Gets the Bitcoin Base-58 format provider. + /// This is also the same format used by Monero and IPFS. + /// + public static readonly Base58FormatProvider Bitcoin = new(0, nameof(Bitcoin), "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); + + /// + /// Gets the Flickr Base-58 format provider. + /// + public static readonly Base58FormatProvider Flickr = new(1, nameof(Flickr), "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"); + + /// + /// Gets the Ripple Base-58 format provider. + /// + public static readonly Base58FormatProvider Ripple = new(2, nameof(Ripple), "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"); + + /// + /// Initializes a new instance of the class. + /// + /// The value of the enumeration entry. + /// The name of the enumeration entry. + /// The alphabet of the format provider. + private Base58FormatProvider(int value, string name, string alphabet) : base(value, name) + { + Alphabet = alphabet; + } + + /// + /// Gets the alphabet of the current instance. + /// + public string Alphabet { get; } + + /// Gets an object that provides formatting services for the specified type. + /// An object that specifies the type of format object to return. + /// + /// Returns an instance of the object specified by , + /// if the implementation can supply that type of object; otherwise, . + /// + public object? GetFormat(Type? formatType) + { + return formatType == typeof(Base58FormatProvider) ? this : null; + } +} diff --git a/OnixLabs.Core/Text/Base64.Equatable.cs b/OnixLabs.Core/Text/Base64.Equatable.cs index a039f08..709c1af 100644 --- a/OnixLabs.Core/Text/Base64.Equatable.cs +++ b/OnixLabs.Core/Text/Base64.Equatable.cs @@ -1,73 +1,72 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Linq; using OnixLabs.Core.Linq; namespace OnixLabs.Core.Text; -public readonly partial struct Base64 : IEquatable +public readonly partial struct Base64 { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(Base64 a, Base64 b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(Base64 other) { - return Equals(a, b); + return other.value.SequenceEqual(value); } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(Base64 a, Base64 b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return obj is Base64 other && Equals(other); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public bool Equals(Base64 other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return other.Value.SequenceEqual(Value); + return value.GetContentHashCode(); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(Base64 left, Base64 right) { - return obj is Base64 other && Equals(other); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(Base64 left, Base64 right) { - return HashCode.Combine(Value.GetContentHashCode()); + return !Equals(left, right); } } diff --git a/OnixLabs.Core/Text/Base64.Format.cs b/OnixLabs.Core/Text/Base64.Format.cs new file mode 100644 index 0000000..3f3345a --- /dev/null +++ b/OnixLabs.Core/Text/Base64.Format.cs @@ -0,0 +1,33 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +public readonly partial struct Base64 +{ + /// + /// Tries to format the value of the current instance into the provided span of characters. + /// + /// The span in which to write this instance's value formatted as a span of characters. + /// When this method returns, contains the number of characters that were written in . + /// A span containing the characters that represent a standard or custom format string that defines the acceptable format for . + /// An optional object that supplies culture-specific formatting information for . + /// Returns if the formatting was successful; otherwise, . + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + return ToString(format, provider).TryCopyTo(destination, out charsWritten); + } +} diff --git a/OnixLabs.Core/Text/Base64.From.cs b/OnixLabs.Core/Text/Base64.From.cs deleted file mode 100644 index f2fe891..0000000 --- a/OnixLabs.Core/Text/Base64.From.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using System.Text; - -namespace OnixLabs.Core.Text; - -public readonly partial struct Base64 -{ - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Returns a new instance. - public static Base64 FromByteArray(byte[] value) - { - return new Base64(value); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// Returns a new instance. - public static Base64 FromCharArray(char[] value) - { - return FromCharArray(value, Encoding.Default); - } - - /// - /// Creates a instance from the specified array. - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base64 FromCharArray(char[] value, Encoding encoding) - { - byte[] bytes = encoding.GetBytes(value); - return FromByteArray(bytes); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Returns a new instance. - public static Base64 FromSpan(ReadOnlySpan value) - { - return FromSpan(value, Encoding.Default); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base64 FromSpan(ReadOnlySpan value, Encoding encoding) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// Returns a new instance. - public static Base64 FromString(string value) - { - return FromString(value, Encoding.Default); - } - - /// - /// Creates a instance from the specified . - /// - /// The underlying value. - /// The encoding to use to obtain the underlying value. - /// Returns a new instance. - public static Base64 FromString(string value, Encoding encoding) - { - char[] characters = value.ToArray(); - return FromCharArray(characters, encoding); - } -} diff --git a/OnixLabs.Core/Text/Base64.Parse.cs b/OnixLabs.Core/Text/Base64.Parse.cs index 89292d3..89a0bb5 100644 --- a/OnixLabs.Core/Text/Base64.Parse.cs +++ b/OnixLabs.Core/Text/Base64.Parse.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,35 +19,62 @@ namespace OnixLabs.Core.Text; public readonly partial struct Base64 { /// - /// Parses a Base-64 value into a instance. + /// Parses the specified Base-64 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// Returns a new instance. - public static Base64 Parse(string value) + /// The Base-64 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// Returns a new instance, parsed from the specified Base-64 encoded value. + public static Base64 Parse(string value, IFormatProvider? provider = null) { - char[] characters = value.ToCharArray(); - return Parse(characters); + return Parse(value.AsSpan(), provider); } /// - /// Parses a Base-64 value into a instance. + /// Parses the specified Base-64 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// Returns a new instance. - public static Base64 Parse(char[] value) + /// The Base-64 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// Returns a new instance, parsed from the specified Base-64 encoded value. + public static Base64 Parse(ReadOnlySpan value, IFormatProvider? provider = null) { - byte[] bytes = Convert.FromBase64CharArray(value, 0, value.Length); - return FromByteArray(bytes); + byte[] bytes = IBaseCodec.Base64.Decode(value, provider); + return new Base64(bytes); } /// - /// Parses a Base-64 value into a instance. + /// Tries to parse the specified Base-64 encoded value into a value. /// - /// The Base-16 (hexadecimal) value to parse. - /// Returns a new instance. - public static Base64 Parse(ReadOnlySpan value) + /// The Base-64 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// + /// A new instance, parsed from the specified Base-64 encoded value, + /// or the default value if the specified Base-64 encoded could not be parsed. + /// + /// Returns if the specified Base-64 value was decoded successfully; otherwise, . + public static bool TryParse(string? value, IFormatProvider? provider, out Base64 result) { - char[] characters = value.ToArray(); - return Parse(characters); + return TryParse(value.AsSpan(), provider, out result); + } + + /// + /// Tries to parse the specified Base-64 encoded value into a value. + /// + /// The Base-64 encoded value to parse. + /// The format provider that will be used to decode the specified value. + /// + /// A new instance, parsed from the specified Base-64 encoded value, + /// or the default value if the specified Base-64 encoded could not be parsed. + /// + /// Returns if the specified Base-64 value was decoded successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, IFormatProvider? provider, out Base64 result) + { + if (IBaseCodec.Base64.TryDecode(value, provider, out byte[] bytes)) + { + result = new Base64(bytes); + return true; + } + + result = default; + return false; } } diff --git a/OnixLabs.Core/Text/Base64.To.cs b/OnixLabs.Core/Text/Base64.To.cs index faea21b..4ba8048 100644 --- a/OnixLabs.Core/Text/Base64.To.cs +++ b/OnixLabs.Core/Text/Base64.To.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,46 +13,58 @@ // limitations under the License. using System; -using System.Text; namespace OnixLabs.Core.Text; public readonly partial struct Base64 { /// - /// Returns a array that represents the current object. + /// Gets the underlying representation of the current instance. /// - /// Returns a array that represents the current object. + /// Return the underlying representation of the current instance. public byte[] ToByteArray() { - return Value.Copy(); + return value.Copy(); } /// - /// Returns a that represents the current object in plain text. + /// Returns a that represents the current object. /// - /// Returns a that represents the current object in plain text. - public string ToPlainTextString() + /// Returns a that represents the current object. + public override string ToString() { - return ToPlainTextString(Encoding.Default); + return ToString(null); } /// - /// Returns a that represents the current object in plain text. + /// Formats the value of the current instance using the specified format. /// - /// The encoding to use to obtain the underlying value. - /// Returns a that represents the current object in plain text. - public string ToPlainTextString(Encoding encoding) + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(IFormatProvider? formatProvider) { - return encoding.GetString(Value); + return ToString(null, formatProvider); } /// - /// Returns a that represents the current object. + /// Formats the value of the current instance using the specified format. /// - /// A that represents the current object. - public override string ToString() + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(format.AsSpan(), formatProvider); + } + + /// + /// Formats the value of the current instance using the specified format. + /// + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(ReadOnlySpan format, IFormatProvider? formatProvider) { - return Convert.ToBase64String(Value); + return IBaseCodec.Base64.Encode(value, formatProvider); } } diff --git a/OnixLabs.Core/Text/Base64.cs b/OnixLabs.Core/Text/Base64.cs index 0894e33..09d103f 100644 --- a/OnixLabs.Core/Text/Base64.cs +++ b/OnixLabs.Core/Text/Base64.cs @@ -1,35 +1,33 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Core.Text; /// /// Represents a Base-64 value. /// -public readonly partial struct Base64 +/// The underlying value. +public readonly partial struct Base64(ReadOnlySpan value) : IBaseValue { + private readonly byte[] value = value.ToArray(); + /// - /// Initializes a new instance of the struct. + /// Initializes a new default value. /// - /// The underlying value. - private Base64(byte[] value) + public Base64() : this([]) { - Value = value; } - - /// - /// Gets the underlying value. - /// - private byte[] Value { get; } } diff --git a/OnixLabs.Core/Text/Base64Codec.cs b/OnixLabs.Core/Text/Base64Codec.cs new file mode 100644 index 0000000..38df383 --- /dev/null +++ b/OnixLabs.Core/Text/Base64Codec.cs @@ -0,0 +1,95 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +/// +/// Represents a codec for encoding and decoding Base-64 values. +/// +public sealed class Base64Codec : IBaseCodec +{ + /// + /// Encodes the specified value into a Base-64 representation. + /// + /// The value to encode into a Base-64 representation. + /// The format provider that will be used to encode the specified value. + /// Returns a new Base-64 representation encoded from the specified value. + public string Encode(ReadOnlySpan value, IFormatProvider? provider = null) + { + if (TryEncode(value, provider, out string result)) return result; + throw new FormatException(IBaseCodec.EncodingFormatException); + } + + /// + /// Decodes the specified Base-64 representation into a . + /// + /// The Base-64 value to decode into a . + /// The format provider that will be used to decode the specified value. + /// Returns a new decoded from the specified value. + public byte[] Decode(ReadOnlySpan value, IFormatProvider? provider = null) + { + if (TryDecode(value, provider, out byte[] result)) return result; + throw new FormatException(IBaseCodec.DecodingFormatException); + } + + /// + /// Tries to encode the specified value into a Base-64 representation. + /// + /// The value to encode into a Base-64 representation. + /// The format provider that will be used to encode the specified value. + /// + /// A new Base-64 representation encoded from the specified value, + /// or an empty string if the specified value could not be encoded. + /// + /// Returns if the specified value was encoded successfully; otherwise, . + public bool TryEncode(ReadOnlySpan value, IFormatProvider? provider, out string result) + { + try + { + result = Convert.ToBase64String(value); + return true; + } + catch + { + result = string.Empty; + return false; + } + } + + /// + /// Tries to decode the specified Base-64 representation into a . + /// + /// The Base-64 value to decode into a . + /// The format provider that will be used to decode the specified value. + /// + /// A new decoded from the specified value, + /// or an empty if the specified value could not be decoded. + /// + /// Returns if the specified value was decoded successfully; otherwise, . + public bool TryDecode(ReadOnlySpan value, IFormatProvider? provider, out byte[] result) + { + try + { + result = Convert.FromBase64String(value.ToString()); + return true; + } + catch + { + result = []; + return false; + } + } +} diff --git a/OnixLabs.Core/Text/Extensions.StringBuilder.cs b/OnixLabs.Core/Text/Extensions.StringBuilder.cs new file mode 100644 index 0000000..0131c9a --- /dev/null +++ b/OnixLabs.Core/Text/Extensions.StringBuilder.cs @@ -0,0 +1,151 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; +using System.Text; +using OnixLabs.Core.Linq; + +namespace OnixLabs.Core.Text; + +/// +/// Provides extension methods for string builders. +/// +// ReSharper disable UnusedMethodReturnValue.Global +[EditorBrowsable(EditorBrowsableState.Never)] +public static class StringBuilderExtensions +{ + /// + /// Appends the specified values to the current . + /// + /// The to append to. + /// The values to append. + /// Returns the current with the specified items appended. + public static StringBuilder Append(this StringBuilder builder, params object[] values) + { + return builder.Append(values.JoinToString(string.Empty)); + } + + /// + /// Prepends the specified values to the current . + /// + /// The to prepend to. + /// The values to prepend. + /// Returns the current with the specified items prepended. + public static StringBuilder Prepend(this StringBuilder builder, params object[] values) + { + return builder.Insert(0, values.JoinToString(string.Empty)); + } + + /// + /// Trims the specified value from the start and end of the current . + /// + /// The to trim. + /// The value to trim. + /// Returns the current with the specified value trimmed from the start and end. + public static StringBuilder Trim(this StringBuilder builder, char value) + { + return builder.TrimStart(value).TrimEnd(value); + } + + /// + /// Trims the specified value from the end of the current . + /// + /// The to trim. + /// The value to trim. + /// Returns the current with the specified value trimmed from the end. + public static StringBuilder TrimEnd(this StringBuilder builder, char value) + { + while (builder.Length > 0 && builder[^1] == value) + builder.Remove(builder.Length - 1, 1); + + return builder; + } + + /// + /// Trims the specified value from the start of the current . + /// + /// The to trim. + /// The value to trim. + /// Returns the current with the specified value trimmed from the start. + public static StringBuilder TrimStart(this StringBuilder builder, char value) + { + while (builder.Length > 0 && builder[0] == value) + builder.Remove(0, 1); + + return builder; + } + + /// + /// Trims the specified value from the start and end of the current . + /// + /// The to trim. + /// The value to trim. + /// Returns the current with the specified value trimmed from the start and end. + public static StringBuilder Trim(this StringBuilder builder, string value) + { + return builder.TrimStart(value).TrimEnd(value); + } + + /// + /// Trims the specified value from the end of the current . + /// + /// The to trim. + /// The value to trim. + /// Returns the current with the specified value trimmed from the end. + public static StringBuilder TrimEnd(this StringBuilder builder, string value) + { + while (builder.Length >= value.Length && builder.ToString(builder.Length - value.Length, value.Length) == value) + builder.Remove(builder.Length - value.Length, value.Length); + + return builder; + } + + /// + /// Trims the specified value from the start of the current . + /// + /// The to trim. + /// The value to trim. + /// Returns the current with the specified value trimmed from the start. + public static StringBuilder TrimStart(this StringBuilder builder, string value) + { + while (builder.Length >= value.Length && builder.ToString(0, value.Length) == value) + builder.Remove(0, value.Length); + + return builder; + } + + /// + /// Wraps the current between the specified start and end values. + /// + /// The to wrap. + /// The value to prepend. + /// The value to append. + /// Returns the current wrapped between the specified start and end values. + public static StringBuilder Wrap(this StringBuilder builder, char start, char end) + { + return builder.Prepend(start).Append(end); + } + + /// + /// Wraps the current between the specified start and end values. + /// + /// The to wrap. + /// The value to prepend. + /// The value to append. + /// Returns the current wrapped between the specified start and end values. + public static StringBuilder Wrap(this StringBuilder builder, string start, string end) + { + return builder.Prepend(start).Append(end); + } +} diff --git a/OnixLabs.Core/Text/IBaseCodec.cs b/OnixLabs.Core/Text/IBaseCodec.cs new file mode 100644 index 0000000..8b487f7 --- /dev/null +++ b/OnixLabs.Core/Text/IBaseCodec.cs @@ -0,0 +1,93 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +/// +/// Defines a codec for encoding and decoding Base-N values. +/// +public interface IBaseCodec +{ + /// + /// The exception message to throw for encoding operations. + /// + protected const string EncodingFormatException = "The specified value was not in the correct format and could not be encoded."; + + /// + /// The exception message to throw for decoding operations. + /// + protected const string DecodingFormatException = "The specified value was not in the correct format and could not be decoded."; + + /// + /// Gets a new instance. + /// + public static Base16Codec Base16 => new(); + + /// + /// Gets a new instance. + /// + public static Base32Codec Base32 => new(); + + /// + /// Gets a new instance. + /// + public static Base58Codec Base58 => new(); + + /// + /// Gets a new instance. + /// + public static Base64Codec Base64 => new(); + + /// + /// Encodes the specified value into a Base-N representation. + /// + /// The value to encode into a Base-N representation. + /// The format provider that will be used to encode the specified value. + /// Returns a new Base-N representation encoded from the specified value. + string Encode(ReadOnlySpan value, IFormatProvider? provider = null); + + /// + /// Decodes the specified Base-N representation into a . + /// + /// The Base-N value to decode into a . + /// The format provider that will be used to decode the specified value. + /// Returns a new decoded from the specified value. + byte[] Decode(ReadOnlySpan value, IFormatProvider? provider = null); + + /// + /// Tries to encode the specified value into a Base-N representation. + /// + /// The value to encode into a Base-N representation. + /// The format provider that will be used to encode the specified value. + /// + /// A new Base-N representation encoded from the specified value, + /// or an empty string if the specified value could not be encoded. + /// + /// Returns if the specified value was encoded successfully; otherwise, . + bool TryEncode(ReadOnlySpan value, IFormatProvider? provider, out string result); + + /// + /// Tries to decode the specified Base-N representation into a . + /// + /// The Base-N value to decode into a . + /// The format provider that will be used to decode the specified value. + /// + /// A new decoded from the specified value, + /// or an empty if the specified value could not be decoded. + /// + /// Returns if the specified value was decoded successfully; otherwise, . + bool TryDecode(ReadOnlySpan value, IFormatProvider? provider, out byte[] result); +} diff --git a/OnixLabs.Core/Text/IBaseValue.cs b/OnixLabs.Core/Text/IBaseValue.cs new file mode 100644 index 0000000..103c244 --- /dev/null +++ b/OnixLabs.Core/Text/IBaseValue.cs @@ -0,0 +1,43 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Core.Text; + +/// +/// Defines a Base-N value. +/// +public interface IBaseValue : IBinaryConvertible, ISpanFormattable +{ + /// + /// Formats the value of the current instance using the specified format. + /// + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + string ToString(IFormatProvider? formatProvider); + + /// + /// Formats the value of the current instance using the specified format. + /// + /// The format to use. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + string ToString(ReadOnlySpan format, IFormatProvider? formatProvider); +} + +/// +/// Defines a generic base encoding representation. +/// +public interface IBaseValue : IValueEquatable, ISpanParsable, IBaseValue where T : struct, IBaseValue; diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticAbsDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticAbsDataAttribute.cs new file mode 100644 index 0000000..1481511 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticAbsDataAttribute.cs @@ -0,0 +1,27 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticAbsDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal value in TestDataGenerator.GenerateRandomValues()) + yield return [value]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticAdditionDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticAdditionDataAttribute.cs new file mode 100644 index 0000000..b31cea4 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticAdditionDataAttribute.cs @@ -0,0 +1,32 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticAdditionDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal left in TestDataGenerator.GenerateScaledValues()) + foreach (decimal right in TestDataGenerator.GenerateScaledValues()) + yield return [left, right, Guid.NewGuid()]; + + foreach (decimal left in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MinValue)) + foreach (decimal right in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MaxValue)) + yield return [left, right, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticDivisionDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticDivisionDataAttribute.cs new file mode 100644 index 0000000..22c87f6 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticDivisionDataAttribute.cs @@ -0,0 +1,40 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticDivisionDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (MidpointRounding mode in TestDataGenerator.GetMidpointRoundingModes()) + foreach (decimal left in TestDataGenerator.GenerateScaledValues()) + foreach (decimal right in TestDataGenerator.GenerateScaledValues()) + { + if (right is 0) continue; + yield return [left, right, mode, Guid.NewGuid()]; + } + + foreach (MidpointRounding mode in TestDataGenerator.GetMidpointRoundingModes()) + foreach (decimal left in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MinValue)) + foreach (decimal right in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MaxValue)) + { + if (right is 0) continue; + yield return [left, right, mode, Guid.NewGuid()]; + } + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticModulusDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticModulusDataAttribute.cs new file mode 100644 index 0000000..85be7d2 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticModulusDataAttribute.cs @@ -0,0 +1,28 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticModulusDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal left in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MinValue)) + foreach (decimal right in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MaxValue)) + yield return [left, right, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticMultiplicationDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticMultiplicationDataAttribute.cs new file mode 100644 index 0000000..a35d844 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticMultiplicationDataAttribute.cs @@ -0,0 +1,32 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticMultiplicationDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal left in TestDataGenerator.GenerateScaledValues()) + foreach (decimal right in TestDataGenerator.GenerateScaledValues()) + yield return [left, right, Guid.NewGuid()]; + + foreach (decimal left in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MinValue)) + foreach (decimal right in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MaxValue)) + yield return [left, right, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticPowDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticPowDataAttribute.cs new file mode 100644 index 0000000..8affc1d --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticPowDataAttribute.cs @@ -0,0 +1,205 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticPowDataAttribute : DataAttribute +{ + private static readonly (decimal Value, int Exponent, decimal Expected)[] Data = + [ + (0m, 1, 0m), + (0m, 2, 0m), + (0m, 3, 0m), + (0m, 4, 0m), + (0m, 5, 0m), + (0m, 10, 0m), + (0m, 28, 0m), + (1m, 1, 1m), + (1m, 2, 1m), + (1m, 3, 1m), + (1m, 4, 1m), + (1m, 5, 1m), + (1m, 10, 1m), + (1m, 28, 1m), + (2m, 1, 2m), + (2m, 2, 4m), + (2m, 3, 8m), + (2m, 4, 16m), + (2m, 5, 32m), + (2m, 10, 1024m), + (3m, 1, 3m), + (3m, 2, 9m), + (3m, 3, 27m), + (3m, 4, 81m), + (3m, 5, 243m), + (3m, 10, 59049m), + (4m, 1, 4m), + (4m, 2, 16m), + (4m, 3, 64m), + (4m, 4, 256m), + (4m, 5, 1024m), + (4m, 10, 1048576m), + (5m, 1, 5m), + (5m, 2, 25m), + (5m, 3, 125m), + (5m, 4, 625m), + (5m, 5, 3125m), + (5m, 10, 9765625m), + (10m, 1, 10m), + (10m, 2, 100m), + (10m, 3, 1000m), + (10m, 4, 10000m), + (10m, 5, 100000m), + (10m, 10, 10000000000m), + (10m, 28, 10000000000000000000000000000m), + (-1m, 1, -1m), + (-1m, 2, -1m), + (-1m, 3, -1m), + (-1m, 4, -1m), + (-1m, 5, -1m), + (-1m, 10, -1m), + (-1m, 28, -1m), + (-2m, 1, -2m), + (-2m, 2, -4m), + (-2m, 3, -8m), + (-2m, 4, -16m), + (-2m, 5, -32m), + (-2m, 10, -1024m), + (-3m, 1, -3m), + (-3m, 2, -9m), + (-3m, 3, -27m), + (-3m, 4, -81m), + (-3m, 5, -243m), + (-3m, 10, -59049m), + (-4m, 1, -4m), + (-4m, 2, -16m), + (-4m, 3, -64m), + (-4m, 4, -256m), + (-4m, 5, -1024m), + (-4m, 10, -1048576m), + (-5m, 1, -5m), + (-5m, 2, -25m), + (-5m, 3, -125m), + (-5m, 4, -625m), + (-5m, 5, -3125m), + (-5m, 10, -9765625m), + (-10m, 1, -10m), + (-10m, 2, -100m), + (-10m, 3, -1000m), + (-10m, 4, -10000m), + (-10m, 5, -100000m), + (-10m, 10, -10000000000m), + (-10m, 28, -10000000000000000000000000000m), + (1m, -1, 1m), + (1m, -2, 1m), + (1m, -3, 1m), + (1m, -4, 1m), + (1m, -5, 1m), + (1m, -10, 1m), + (1m, -28, 1m), + (2.0m, -1, 0.5m), + (2.00m, -1, 0.50m), + (2.000m, -1, 0.500m), + (2.0000m, -1, 0.5000m), + (2.00000m, -1, 0.50000m), + (2.0000000000m, -1, 0.5000000000m), + (2.0000000000000000000000000000m, -1, 0.5000000000000000000000000000m), + (2.0m, -2, 0.2m), + (2.00m, -2, 0.25m), + (2.000m, -2, 0.250m), + (2.0000m, -2, 0.2500m), + (2.00000m, -2, 0.25000m), + (2.0000000000m, -2, 0.2500000000m), + (2.0000000000000000000000000000m, -2, 0.2500000000000000000000000000m), + (2.0m, -3, 0.1m), + (2.00m, -3, 0.12m), + (2.000m, -3, 0.125m), + (2.0000m, -3, 0.1250m), + (2.00000m, -3, 0.12500m), + (2.0000000000m, -3, 0.1250000000m), + (2.000000000000000000000000000000m, -3, 0.1250000000000000000000000000m), + (2.0m, -4, 0.1m), + (2.00m, -4, 0.06m), + (2.000m, -4, 0.062m), + (2.0000m, -4, 0.0625m), + (2.00000m, -4, 0.06250m), + (2.0000000000m, -4, 0.0625000000m), + (2.0000000000000000000000000000m, -4, 0.0625000000000000000000000000m), + (2.0m, -5, 0.0m), + (2.00m, -5, 0.03m), + (2.000m, -5, 0.031m), + (2.0000m, -5, 0.0312m), + (2.00000m, -5, 0.03125m), + (2.0000000000m, -5, 0.0312500000m), + (2.0000000000000000000000000000m, -5, 0.0312500000000000000000000000m), + (2.0m, -10, 0.0m), + (2.00m, -10, 0.00m), + (2.000m, -10, 0.001m), + (2.0000m, -10, 0.0010m), + (2.00000m, -10, 0.00098m), + (2.0000000000m, -10, 0.0009765625m), + (2.0000000000000000000000000000m, -10, 0.0009765625000000000000000000m), + (3.0m, -1, 0.3m), + (3.00m, -1, 0.33m), + (3.000m, -1, 0.333m), + (3.0000m, -1, 0.3333m), + (3.00000m, -1, 0.33333m), + (3.0000000000m, -1, 0.3333333333m), + (3.0000000000000000000000000000m, -1, 0.3333333333333333333333333333m), + (3.0m, -2, 0.1m), + (3.00m, -2, 0.11m), + (3.000m, -2, 0.111m), + (3.0000m, -2, 0.1111m), + (3.00000m, -2, 0.11111m), + (3.0000000000m, -2, 0.1111111111m), + (3.0000000000000000000000000000m, -2, 0.1111111111111111111111111111m), + (3.0m, -3, 0.0m), + (3.00m, -3, 0.04m), + (3.000m, -3, 0.037m), + (3.0000m, -3, 0.0370m), + (3.00000m, -3, 0.03704m), + (3.0000000000m, -3, 0.0370370370m), + (3.0000000000000000000000000000m, -3, 0.0370370370370370370370370370m), + (3.0m, -4, 0.0m), + (3.00m, -4, 0.01m), + (3.000m, -4, 0.012m), + (3.0000m, -4, 0.0123m), + (3.00000m, -4, 0.01235m), + (3.0000000000m, -4, 0.0123456790m), + (3.0000000000000000000000000000m, -4, 0.0123456790123456790123456790m), + (3.0m, -5, 0.0m), + (3.00m, -5, 0.00m), + (3.000m, -5, 0.004m), + (3.0000m, -5, 0.0041m), + (3.00000m, -5, 0.00412m), + (3.0000000000m, -5, 0.0041152263m), + (3.000000000000000000000000000000m, -5, 0.004115226337448559670781893004m), + (3.0m, -10, 0.0m), + (3.00m, -10, 0.00m), + (3.000m, -10, 0.000m), + (3.0000m, -10, 0.0000m), + (3.00000m, -10, 0.00002m), + (3.0000000000m, -10, 0.0000169351m), + (3.0000000000000000000000000000m, -10, 0.000016935087808430286711036597m) + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach ((decimal value, int exponent, decimal expected) in Data) + yield return [value, exponent, expected]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticRoundDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticRoundDataAttribute.cs new file mode 100644 index 0000000..be55d9b --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticRoundDataAttribute.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticRoundDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + IEnumerable values = Enumerable.Empty() + .Concat(TestDataGenerator.GenerateConstantValues()) + .Concat(TestDataGenerator.GenerateScaledMaxValues()) + .Concat(TestDataGenerator.GenerateScaledMinValues()) + .Concat(TestDataGenerator.GenerateRandomValues()); + + foreach (decimal value in values) + foreach (int scale in TestDataGenerator.GenerateScaleValues()) + foreach (MidpointRounding mode in TestDataGenerator.GetMidpointRoundingModes()) + yield return [value, scale, mode, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticScaleDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticScaleDataAttribute.cs new file mode 100644 index 0000000..b582a74 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticScaleDataAttribute.cs @@ -0,0 +1,59 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticScaleDataAttribute : DataAttribute +{ + private static readonly int[] Integers = [0, 1, 2, 3, 123, 123456789, -1, -2, -3, -123, -123456789]; + + private static readonly int[] IntegerScales = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + + private static readonly decimal[] Decimals = + [ + +0.1234567890987654321000000000m, + +1.1234567890987654321000000000m, + -0.1234567890987654321000000000m, + -1.1234567890987654321000000000m + ]; + + private static readonly int[] DecimalScales = + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (int value in Integers) + foreach (int initialScale in IntegerScales) + foreach (MidpointRounding rounding in Enum.GetValues()) + foreach (int desiredScale in IntegerScales) + { + decimal initial = value.ToDecimal(initialScale, ScaleMode.Integral); + decimal expected = value.ToDecimal(desiredScale, ScaleMode.Integral); + yield return [initial, desiredScale, rounding, expected]; + } + + foreach (decimal value in Decimals) + foreach (int scale in DecimalScales) + foreach (MidpointRounding rounding in Enum.GetValues()) + { + decimal expected = decimal.Round(value, scale, rounding); + yield return [value, scale, rounding, expected]; + } + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticSubtractionDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticSubtractionDataAttribute.cs new file mode 100644 index 0000000..7eec7e1 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticSubtractionDataAttribute.cs @@ -0,0 +1,32 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticSubtractionDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal left in TestDataGenerator.GenerateScaledValues()) + foreach (decimal right in TestDataGenerator.GenerateScaledValues()) + yield return [left, right, Guid.NewGuid()]; + + foreach (decimal left in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MinValue)) + foreach (decimal right in TestDataGenerator.GenerateRandomValues(count: 10, seed: int.MaxValue)) + yield return [left, right, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticTrimDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticTrimDataAttribute.cs new file mode 100644 index 0000000..59dbb6f --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticTrimDataAttribute.cs @@ -0,0 +1,42 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticTrimDataAttribute : DataAttribute +{ + private static readonly (decimal Value, decimal Expected)[] Data = + [ + (0.0m, 0m), + (1.0m, 1m), + (1.0000000000000000000000000000m, 1m), + (1.10m, 1.1m), + (1.1000000000000000000000000000m, 1.1m), + (123.4560000000000000000000000000m, 123.456m), + (-1.0m, -1m), + (-1.0000000000000000000000000000m, -1m), + (-1.10m, -1.1m), + (-1.1000000000000000000000000000m, -1.1m), + (-123.4560000000000000000000000000m, -123.456m) + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach ((decimal value, decimal expected) in Data) + yield return [value, expected]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticTruncateDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticTruncateDataAttribute.cs new file mode 100644 index 0000000..925df4f --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticTruncateDataAttribute.cs @@ -0,0 +1,30 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticTruncateDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal value in TestDataGenerator.GenerateScaledValues()) + yield return [value, Guid.NewGuid()]; + + foreach (decimal value in TestDataGenerator.GenerateRandomValues()) + yield return [value, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticUnaryAdditionDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticUnaryAdditionDataAttribute.cs new file mode 100644 index 0000000..6d6189b --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticUnaryAdditionDataAttribute.cs @@ -0,0 +1,27 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticUnaryAdditionDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal value in TestDataGenerator.GenerateRandomValues()) + yield return [value]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticUnarySubtractionDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticUnarySubtractionDataAttribute.cs new file mode 100644 index 0000000..d345e19 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalArithmeticUnarySubtractionDataAttribute.cs @@ -0,0 +1,27 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalArithmeticUnarySubtractionDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal value in TestDataGenerator.GenerateRandomValues()) + yield return [value]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalIeee754BinaryDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalIeee754BinaryDataAttribute.cs new file mode 100644 index 0000000..1ab8a0f --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalIeee754BinaryDataAttribute.cs @@ -0,0 +1,217 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalIeee754BinaryDataAttribute : DataAttribute +{ + private static readonly double[] Data = + [ + // General test values + 0.0, double.MinValue, double.MaxValue, double.E, double.Epsilon, double.Pi, double.Tau, + + // Positive one with positive exponent + 1.0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, + 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 1e+100, 1e+308, + + // Positive one with negative exponent + 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, + 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19, 1e-20, 1e-100, 1e-320, + + // Positive two with positive exponent + 2.0, 2e+1, 2e+2, 2e+3, 2e+4, 2e+5, 2e+6, 2e+7, 2e+8, 2e+9, 2e+10, + 2e+11, 2e+12, 2e+13, 2e+14, 2e+15, 2e+16, 2e+17, 2e+18, 2e+19, 2e+20, 2e+100, 2e+307, + + // Positive two with negative exponent + 2e-1, 2e-2, 2e-3, 2e-4, 2e-5, 2e-6, 2e-7, 2e-8, 2e-9, 2e-10, + 2e-11, 2e-12, 2e-13, 2e-14, 2e-15, 2e-16, 2e-17, 2e-18, 2e-19, 2e-20, 2e-100, 2e-320, + + // Positive three with positive exponent + 3.0, 3e+1, 3e+2, 3e+3, 3e+4, 3e+5, 3e+6, 3e+7, 3e+8, 3e+9, 3e+10, + 3e+11, 3e+12, 3e+13, 3e+14, 3e+15, 3e+16, 3e+17, 3e+18, 3e+19, 3e+20, 3e+100, 3e+307, + + // Positive three with negative exponent + 3e-1, 3e-2, 3e-3, 3e-4, 3e-5, 3e-6, 3e-7, 3e-8, 3e-9, 3e-10, + 3e-11, 3e-12, 3e-13, 3e-14, 3e-15, 3e-16, 3e-17, 3e-18, 3e-19, 3e-20, 3e-100, 3e-320, + + // Positive four with positive exponent + 4.0, 4e+1, 4e+2, 4e+3, 4e+4, 4e+5, 4e+6, 4e+7, 4e+8, 4e+9, 4e+10, + 4e+11, 4e+12, 4e+13, 4e+14, 4e+15, 4e+16, 4e+17, 4e+18, 4e+19, 4e+20, 4e+100, 4e+307, + + // Positive four with negative exponent + 4e-1, 4e-2, 4e-3, 4e-4, 4e-5, 4e-6, 4e-7, 4e-8, 4e-9, 4e-10, + 4e-11, 4e-12, 4e-13, 4e-14, 4e-15, 4e-16, 4e-17, 4e-18, 4e-19, 4e-20, 4e-100, 4e-320, + + // Positive five with positive exponent + 5.0, 5e+1, 5e+2, 5e+3, 5e+4, 5e+5, 5e+6, 5e+7, 5e+8, 5e+9, 5e+10, + 5e+11, 5e+12, 5e+13, 5e+14, 5e+15, 5e+16, 5e+17, 5e+18, 5e+19, 5e+20, 5e+100, 5e+307, + + // Positive five with negative exponent + 5e-1, 5e-2, 5e-3, 5e-4, 5e-5, 5e-6, 5e-7, 5e-8, 5e-9, 5e-10, + 5e-11, 5e-12, 5e-13, 5e-14, 5e-15, 5e-16, 5e-17, 5e-18, 5e-19, 5e-20, 5e-100, 5e-320, + + // Positive six with positive exponent + 6.0, 6e+1, 6e+2, 6e+3, 6e+4, 6e+5, 6e+6, 6e+7, 6e+8, 6e+9, 6e+10, + 6e+11, 6e+12, 6e+13, 6e+14, 6e+15, 6e+16, 6e+17, 6e+18, 6e+19, 6e+20, 6e+100, 6e+307, + + // Positive six with negative exponent + 6e-1, 6e-2, 6e-3, 6e-4, 6e-5, 6e-6, 6e-7, 6e-8, 6e-9, 6e-10, + 6e-11, 6e-12, 6e-13, 6e-14, 6e-15, 6e-16, 6e-17, 6e-18, 6e-19, 6e-20, 6e-100, 6e-320, + + // Positive seven with positive exponent + 7.0, 7e+1, 7e+2, 7e+3, 7e+4, 7e+5, 7e+6, 7e+7, 7e+8, 7e+9, 7e+10, + 7e+11, 7e+12, 7e+13, 7e+14, 7e+15, 7e+16, 7e+17, 7e+18, 7e+19, 7e+20, 7e+100, 7e+307, + + // Positive seven with negative exponent + 7e-1, 7e-2, 7e-3, 7e-4, 7e-5, 7e-6, 7e-7, 7e-8, 7e-9, 7e-10, + 7e-11, 7e-12, 7e-13, 7e-14, 7e-15, 7e-16, 7e-17, 7e-18, 7e-19, 7e-20, 7e-100, 7e-320, + + // Positive eight with positive exponent + 8.0, 8e+1, 8e+2, 8e+3, 8e+4, 8e+5, 8e+6, 8e+7, 8e+8, 8e+9, 8e+10, + 8e+11, 8e+12, 8e+13, 8e+14, 8e+15, 8e+16, 8e+17, 8e+18, 8e+19, 8e+20, 8e+100, 8e+307, + + // Positive eight with negative exponent + 8e-1, 8e-2, 8e-3, 8e-4, 8e-5, 8e-6, 8e-7, 8e-8, 8e-9, 8e-10, + 8e-11, 8e-12, 8e-13, 8e-14, 8e-15, 8e-16, 8e-17, 8e-18, 8e-19, 8e-20, 8e-100, 8e-320, + + // Positive nine with positive exponent + 9.0, 9e+1, 9e+2, 9e+3, 9e+4, 9e+5, 9e+6, 9e+7, 9e+8, 9e+9, 9e+10, + 9e+11, 9e+12, 9e+13, 9e+14, 9e+15, 9e+16, 9e+17, 9e+18, 9e+19, 9e+20, 9e+100, 9e+307, + + // Positive nine with negative exponent + 9e-1, 9e-2, 9e-3, 9e-4, 9e-5, 9e-6, 9e-7, 9e-8, 9e-9, 9e-10, + 9e-11, 9e-12, 9e-13, 9e-14, 9e-15, 9e-16, 9e-17, 9e-18, 9e-19, 9e-20, 9e-100, 9e-320, + + // Negative one with positive exponent + -1.0, -1e+1, -1e+2, -1e+3, -1e+4, -1e+5, -1e+6, -1e+7, -1e+8, -1e+9, -1e+10, + -1e+11, -1e+12, -1e+13, -1e+14, -1e+15, -1e+16, -1e+17, -1e+18, -1e+19, -1e+20, -1e+100, -1e+308, + + // Negative one with negative exponent + -1e-1, -1e-2, -1e-3, -1e-4, -1e-5, -1e-6, -1e-7, -1e-8, -1e-9, -1e-10, + -1e-11, -1e-12, -1e-13, -1e-14, -1e-15, -1e-16, -1e-17, -1e-18, -1e-19, -1e-20, -1e-100, -1e-320, + + // Negative two with positive exponent + -2.0, -2e+1, -2e+2, -2e+3, -2e+4, -2e+5, -2e+6, -2e+7, -2e+8, -2e+9, -2e+10, + -2e+11, -2e+12, -2e+13, -2e+14, -2e+15, -2e+16, -2e+17, -2e+18, -2e+19, -2e+20, -2e+100, -2e+307, + + // Negative two with negative exponent + -2e-1, -2e-2, -2e-3, -2e-4, -2e-5, -2e-6, -2e-7, -2e-8, -2e-9, -2e-10, + -2e-11, -2e-12, -2e-13, -2e-14, -2e-15, -2e-16, -2e-17, -2e-18, -2e-19, -2e-20, -2e-100, -2e-320, + + // Negative three with positive exponent + -3.0, -3e+1, -3e+2, -3e+3, -3e+4, -3e+5, -3e+6, -3e+7, -3e+8, -3e+9, -3e+10, + -3e+11, -3e+12, -3e+13, -3e+14, -3e+15, -3e+16, -3e+17, -3e+18, -3e+19, -3e+20, -3e+100, -3e+307, + + // Negative three with negative exponent + -3e-1, -3e-2, -3e-3, -3e-4, -3e-5, -3e-6, -3e-7, -3e-8, -3e-9, -3e-10, + -3e-11, -3e-12, -3e-13, -3e-14, -3e-15, -3e-16, -3e-17, -3e-18, -3e-19, -3e-20, -3e-100, -3e-320, + + // Negative four with positive exponent + -4.0, -4e+1, -4e+2, -4e+3, -4e+4, -4e+5, -4e+6, -4e+7, -4e+8, -4e+9, -4e+10, + -4e+11, -4e+12, -4e+13, -4e+14, -4e+15, -4e+16, -4e+17, -4e+18, -4e+19, -4e+20, -4e+100, -4e+307, + + // Negative four with negative exponent + -4e-1, -4e-2, -4e-3, -4e-4, -4e-5, -4e-6, -4e-7, -4e-8, -4e-9, -4e-10, + -4e-11, -4e-12, -4e-13, -4e-14, -4e-15, -4e-16, -4e-17, -4e-18, -4e-19, -4e-20, -4e-100, -4e-320, + + // Negative five with positive exponent + -5.0, -5e+1, -5e+2, -5e+3, -5e+4, -5e+5, -5e+6, -5e+7, -5e+8, -5e+9, -5e+10, + -5e+11, -5e+12, -5e+13, -5e+14, -5e+15, -5e+16, -5e+17, -5e+18, -5e+19, -5e+20, -5e+100, -5e+307, + + // Negative five with negative exponent + -5e-1, -5e-2, -5e-3, -5e-4, -5e-5, -5e-6, -5e-7, -5e-8, -5e-9, -5e-10, + -5e-11, -5e-12, -5e-13, -5e-14, -5e-15, -5e-16, -5e-17, -5e-18, -5e-19, -5e-20, -5e-100, -5e-320, + + // Negative six with positive exponent + -6.0, -6e+1, -6e+2, -6e+3, -6e+4, -6e+5, -6e+6, -6e+7, -6e+8, -6e+9, -6e+10, + -6e+11, -6e+12, -6e+13, -6e+14, -6e+15, -6e+16, -6e+17, -6e+18, -6e+19, -6e+20, -6e+100, -6e+307, + + // Negative six with negative exponent + -6e-1, -6e-2, -6e-3, -6e-4, -6e-5, -6e-6, -6e-7, -6e-8, -6e-9, -6e-10, + -6e-11, -6e-12, -6e-13, -6e-14, -6e-15, -6e-16, -6e-17, -6e-18, -6e-19, -6e-20, -6e-100, -6e-320, + + // Negative seven with positive exponent + -7.0, -7e+1, -7e+2, -7e+3, -7e+4, -7e+5, -7e+6, -7e+7, -7e+8, -7e+9, -7e+10, + -7e+11, -7e+12, -7e+13, -7e+14, -7e+15, -7e+16, -7e+17, -7e+18, -7e+19, -7e+20, -7e+100, -7e+307, + + // Negative seven with negative exponent + -7e-1, -7e-2, -7e-3, -7e-4, -7e-5, -7e-6, -7e-7, -7e-8, -7e-9, -7e-10, + -7e-11, -7e-12, -7e-13, -7e-14, -7e-15, -7e-16, -7e-17, -7e-18, -7e-19, -7e-20, -7e-100, -7e-320, + + // Negative eight with positive exponent + -8.0, -8e+1, -8e+2, -8e+3, -8e+4, -8e+5, -8e+6, -8e+7, -8e+8, -8e+9, -8e+10, + -8e+11, -8e+12, -8e+13, -8e+14, -8e+15, -8e+16, -8e+17, -8e+18, -8e+19, -8e+20, -8e+100, -8e+307, + + // Negative eight with negative exponent + -8e-1, -8e-2, -8e-3, -8e-4, -8e-5, -8e-6, -8e-7, -8e-8, -8e-9, -8e-10, + -8e-11, -8e-12, -8e-13, -8e-14, -8e-15, -8e-16, -8e-17, -8e-18, -8e-19, -8e-20, -8e-100, -8e-320, + + // Negative nine with positive exponent + -9.0, -9e+1, -9e+2, -9e+3, -9e+4, -9e+5, -9e+6, -9e+7, -9e+8, -9e+9, -9e+10, + -9e+11, -9e+12, -9e+13, -9e+14, -9e+15, -9e+16, -9e+17, -9e+18, -9e+19, -9e+20, -9e+100, -9e+307, + + // Negative nine with negative exponent + -9e-1, -9e-2, -9e-3, -9e-4, -9e-5, -9e-6, -9e-7, -9e-8, -9e-9, -9e-10, + -9e-11, -9e-12, -9e-13, -9e-14, -9e-15, -9e-16, -9e-17, -9e-18, -9e-19, -9e-20, -9e-100, -9e-320, + + // Positive point shifting + 1234567890123456, + 123456789012345.6, + 12345678901234.56, + 1234567890123.456, + 123456789012.3456, + 12345678901.23456, + 1234567890.123456, + 123456789.0123456, + 12345678.90123456, + 1234567.890123456, + 123456.7890123456, + 12345.67890123456, + 1234.567890123456, + 123.4567890123456, + 12.34567890123456, + 1.234567890123456, + + // Negative point shifting + -1234567890123456, + -123456789012345.6, + -12345678901234.56, + -1234567890123.456, + -123456789012.3456, + -12345678901.23456, + -1234567890.123456, + -123456789.0123456, + -12345678.90123456, + -1234567.890123456, + -123456.7890123456, + -12345.67890123456, + -1234.567890123456, + -123.4567890123456, + -12.34567890123456, + -1.234567890123456, + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (double value in Data) yield return [value, GetNumberFormattedAsInflatedFixedPoint(value)]; + } + + private static string GetNumberFormattedAsInflatedFixedPoint(double value) + { + return value.ToString("F5000").TrimEnd('0').TrimEnd('.'); + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalIeee754DecimalDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalIeee754DecimalDataAttribute.cs new file mode 100644 index 0000000..ffdf9cc --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalIeee754DecimalDataAttribute.cs @@ -0,0 +1,915 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalIeee754DecimalDataAttribute : DataAttribute +{ + private static readonly (double Value, string Expected)[] Data = + [ + (double.MinValue, + "-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (double.MaxValue, + "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (double.E, "2.718281828459045"), + (double.Pi, "3.141592653589793"), + (0.0, "0"), + (1.0, "1"), + (1e+1, "10"), + (1e+2, "100"), + (1e+3, "1000"), + (1e+4, "10000"), + (1e+5, "100000"), + (1e+6, "1000000"), + (1e+7, "10000000"), + (1e+8, "100000000"), + (1e+9, "1000000000"), + (1e+10, "10000000000"), + (1e+11, "100000000000"), + (1e+12, "1000000000000"), + (1e+13, "10000000000000"), + (1e+14, "100000000000000"), + (1e+15, "1000000000000000"), + (1e+16, "10000000000000000"), + (1e+17, "100000000000000000"), + (1e+18, "1000000000000000000"), + (1e+19, "10000000000000000000"), + (1e+20, "100000000000000000000"), + (1e+100, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (1e+308, + "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (1e-1, "0.1"), + (1e-2, "0.01"), + (1e-3, "0.001"), + (1e-4, "0.0001"), + (1e-5, "0.00001"), + (1e-6, "0.000001"), + (1e-7, "0.0000001"), + (1e-8, "0.00000001"), + (1e-9, "0.000000001"), + (1e-10, "0.0000000001"), + (1e-11, "0.00000000001"), + (1e-12, "0.000000000001"), + (1e-13, "0.0000000000001"), + (1e-14, "0.00000000000001"), + (1e-15, "0.000000000000001"), + (1e-16, "0.0000000000000001"), + (1e-17, "0.00000000000000001"), + (1e-18, "0.000000000000000001"), + (1e-19, "0.0000000000000000001"), + (1e-20, "0.00000000000000000001"), + (1e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), + (1e-317, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), + (2.0, "2"), + (2e+1, "20"), + (2e+2, "200"), + (2e+3, "2000"), + (2e+4, "20000"), + (2e+5, "200000"), + (2e+6, "2000000"), + (2e+7, "20000000"), + (2e+8, "200000000"), + (2e+9, "2000000000"), + (2e+10, "20000000000"), + (2e+11, "200000000000"), + (2e+12, "2000000000000"), + (2e+13, "20000000000000"), + (2e+14, "200000000000000"), + (2e+15, "2000000000000000"), + (2e+16, "20000000000000000"), + (2e+17, "200000000000000000"), + (2e+18, "2000000000000000000"), + (2e+19, "20000000000000000000"), + (2e+20, "200000000000000000000"), + (2e+100, "20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (2e+307, + "20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (2e-1, "0.2"), + (2e-2, "0.02"), + (2e-3, "0.002"), + (2e-4, "0.0002"), + (2e-5, "0.00002"), + (2e-6, "0.000002"), + (2e-7, "0.0000002"), + (2e-8, "0.00000002"), + (2e-9, "0.000000002"), + (2e-10, "0.0000000002"), + (2e-11, "0.00000000002"), + (2e-12, "0.000000000002"), + (2e-13, "0.0000000000002"), + (2e-14, "0.00000000000002"), + (2e-15, "0.000000000000002"), + (2e-16, "0.0000000000000002"), + (2e-17, "0.00000000000000002"), + (2e-18, "0.000000000000000002"), + (2e-19, "0.0000000000000000002"), + (2e-20, "0.00000000000000000002"), + (2e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"), + (2e-320, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"), + (3.0, "3"), + (3e+1, "30"), + (3e+2, "300"), + (3e+3, "3000"), + (3e+4, "30000"), + (3e+5, "300000"), + (3e+6, "3000000"), + (3e+7, "30000000"), + (3e+8, "300000000"), + (3e+9, "3000000000"), + (3e+10, "30000000000"), + (3e+11, "300000000000"), + (3e+12, "3000000000000"), + (3e+13, "30000000000000"), + (3e+14, "300000000000000"), + (3e+15, "3000000000000000"), + (3e+16, "30000000000000000"), + (3e+17, "300000000000000000"), + (3e+18, "3000000000000000000"), + (3e+19, "30000000000000000000"), + (3e+20, "300000000000000000000"), + (3e+100, "30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (3e+307, + "30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (3e-1, "0.3"), + (3e-2, "0.03"), + (3e-3, "0.003"), + (3e-4, "0.0003"), + (3e-5, "0.00003"), + (3e-6, "0.000003"), + (3e-7, "0.0000003"), + (3e-8, "0.00000003"), + (3e-9, "0.000000003"), + (3e-10, "0.0000000003"), + (3e-11, "0.00000000003"), + (3e-12, "0.000000000003"), + (3e-13, "0.0000000000003"), + (3e-14, "0.00000000000003"), + (3e-15, "0.000000000000003"), + (3e-16, "0.0000000000000003"), + (3e-17, "0.00000000000000003"), + (3e-18, "0.000000000000000003"), + (3e-19, "0.0000000000000000003"), + (3e-20, "0.00000000000000000003"), + (3e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"), + (3e-320, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"), + (4.0, "4"), + (4e+1, "40"), + (4e+2, "400"), + (4e+3, "4000"), + (4e+4, "40000"), + (4e+5, "400000"), + (4e+6, "4000000"), + (4e+7, "40000000"), + (4e+8, "400000000"), + (4e+9, "4000000000"), + (4e+10, "40000000000"), + (4e+11, "400000000000"), + (4e+12, "4000000000000"), + (4e+13, "40000000000000"), + (4e+14, "400000000000000"), + (4e+15, "4000000000000000"), + (4e+16, "40000000000000000"), + (4e+17, "400000000000000000"), + (4e+18, "4000000000000000000"), + (4e+19, "40000000000000000000"), + (4e+20, "400000000000000000000"), + (4e+100, "40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (4e+307, + "40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (4e-1, "0.4"), + (4e-2, "0.04"), + (4e-3, "0.004"), + (4e-4, "0.0004"), + (4e-5, "0.00004"), + (4e-6, "0.000004"), + (4e-7, "0.0000004"), + (4e-8, "0.00000004"), + (4e-9, "0.000000004"), + (4e-10, "0.0000000004"), + (4e-11, "0.00000000004"), + (4e-12, "0.000000000004"), + (4e-13, "0.0000000000004"), + (4e-14, "0.00000000000004"), + (4e-15, "0.000000000000004"), + (4e-16, "0.0000000000000004"), + (4e-17, "0.00000000000000004"), + (4e-18, "0.000000000000000004"), + (4e-19, "0.0000000000000000004"), + (4e-20, "0.00000000000000000004"), + (4e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004"), + (4e-320, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004"), + (5.0, "5"), + (5e+1, "50"), + (5e+2, "500"), + (5e+3, "5000"), + (5e+4, "50000"), + (5e+5, "500000"), + (5e+6, "5000000"), + (5e+7, "50000000"), + (5e+8, "500000000"), + (5e+9, "5000000000"), + (5e+10, "50000000000"), + (5e+11, "500000000000"), + (5e+12, "5000000000000"), + (5e+13, "50000000000000"), + (5e+14, "500000000000000"), + (5e+15, "5000000000000000"), + (5e+16, "50000000000000000"), + (5e+17, "500000000000000000"), + (5e+18, "5000000000000000000"), + (5e+19, "50000000000000000000"), + (5e+20, "500000000000000000000"), + (5e+100, "50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (5e+307, + "50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (5e-1, "0.5"), + (5e-2, "0.05"), + (5e-3, "0.005"), + (5e-4, "0.0005"), + (5e-5, "0.00005"), + (5e-6, "0.000005"), + (5e-7, "0.0000005"), + (5e-8, "0.00000005"), + (5e-9, "0.000000005"), + (5e-10, "0.0000000005"), + (5e-11, "0.00000000005"), + (5e-12, "0.000000000005"), + (5e-13, "0.0000000000005"), + (5e-14, "0.00000000000005"), + (5e-15, "0.000000000000005"), + (5e-16, "0.0000000000000005"), + (5e-17, "0.00000000000000005"), + (5e-18, "0.000000000000000005"), + (5e-19, "0.0000000000000000005"), + (5e-20, "0.00000000000000000005"), + (5e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"), + (5e-320, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"), + (6.0, "6"), + (6e+1, "60"), + (6e+2, "600"), + (6e+3, "6000"), + (6e+4, "60000"), + (6e+5, "600000"), + (6e+6, "6000000"), + (6e+7, "60000000"), + (6e+8, "600000000"), + (6e+9, "6000000000"), + (6e+10, "60000000000"), + (6e+11, "600000000000"), + (6e+12, "6000000000000"), + (6e+13, "60000000000000"), + (6e+14, "600000000000000"), + (6e+15, "6000000000000000"), + (6e+16, "60000000000000000"), + (6e+17, "600000000000000000"), + (6e+18, "6000000000000000000"), + (6e+19, "60000000000000000000"), + (6e+20, "600000000000000000000"), + (6e+100, "60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (6e+307, + "60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (6e-1, "0.6"), + (6e-2, "0.06"), + (6e-3, "0.006"), + (6e-4, "0.0006"), + (6e-5, "0.00006"), + (6e-6, "0.000006"), + (6e-7, "0.0000006"), + (6e-8, "0.00000006"), + (6e-9, "0.000000006"), + (6e-10, "0.0000000006"), + (6e-11, "0.00000000006"), + (6e-12, "0.000000000006"), + (6e-13, "0.0000000000006"), + (6e-14, "0.00000000000006"), + (6e-15, "0.000000000000006"), + (6e-16, "0.0000000000000006"), + (6e-17, "0.00000000000000006"), + (6e-18, "0.000000000000000006"), + (6e-19, "0.0000000000000000006"), + (6e-20, "0.00000000000000000006"), + (6e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006"), + (6e-320, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006"), + (7.0, "7"), + (7e+1, "70"), + (7e+2, "700"), + (7e+3, "7000"), + (7e+4, "70000"), + (7e+5, "700000"), + (7e+6, "7000000"), + (7e+7, "70000000"), + (7e+8, "700000000"), + (7e+9, "7000000000"), + (7e+10, "70000000000"), + (7e+11, "700000000000"), + (7e+12, "7000000000000"), + (7e+13, "70000000000000"), + (7e+14, "700000000000000"), + (7e+15, "7000000000000000"), + (7e+16, "70000000000000000"), + (7e+17, "700000000000000000"), + (7e+18, "7000000000000000000"), + (7e+19, "70000000000000000000"), + (7e+20, "700000000000000000000"), + (7e+100, "70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (7e+307, + "70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (7e-1, "0.7"), + (7e-2, "0.07"), + (7e-3, "0.007"), + (7e-4, "0.0007"), + (7e-5, "0.00007"), + (7e-6, "0.000007"), + (7e-7, "0.0000007"), + (7e-8, "0.00000007"), + (7e-9, "0.000000007"), + (7e-10, "0.0000000007"), + (7e-11, "0.00000000007"), + (7e-12, "0.000000000007"), + (7e-13, "0.0000000000007"), + (7e-14, "0.00000000000007"), + (7e-15, "0.000000000000007"), + (7e-16, "0.0000000000000007"), + (7e-17, "0.00000000000000007"), + (7e-18, "0.000000000000000007"), + (7e-19, "0.0000000000000000007"), + (7e-20, "0.00000000000000000007"), + (7e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007"), + (7e-320, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007"), + (8.0, "8"), + (8e+1, "80"), + (8e+2, "800"), + (8e+3, "8000"), + (8e+4, "80000"), + (8e+5, "800000"), + (8e+6, "8000000"), + (8e+7, "80000000"), + (8e+8, "800000000"), + (8e+9, "8000000000"), + (8e+10, "80000000000"), + (8e+11, "800000000000"), + (8e+12, "8000000000000"), + (8e+13, "80000000000000"), + (8e+14, "800000000000000"), + (8e+15, "8000000000000000"), + (8e+16, "80000000000000000"), + (8e+17, "800000000000000000"), + (8e+18, "8000000000000000000"), + (8e+19, "80000000000000000000"), + (8e+20, "800000000000000000000"), + (8e+100, "80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (8e+307, + "80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (8e-1, "0.8"), + (8e-2, "0.08"), + (8e-3, "0.008"), + (8e-4, "0.0008"), + (8e-5, "0.00008"), + (8e-6, "0.000008"), + (8e-7, "0.0000008"), + (8e-8, "0.00000008"), + (8e-9, "0.000000008"), + (8e-10, "0.0000000008"), + (8e-11, "0.00000000008"), + (8e-12, "0.000000000008"), + (8e-13, "0.0000000000008"), + (8e-14, "0.00000000000008"), + (8e-15, "0.000000000000008"), + (8e-16, "0.0000000000000008"), + (8e-17, "0.00000000000000008"), + (8e-18, "0.000000000000000008"), + (8e-19, "0.0000000000000000008"), + (8e-20, "0.00000000000000000008"), + (8e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008"), + (8e-320, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008"), + (9.0, "9"), + (9e+1, "90"), + (9e+2, "900"), + (9e+3, "9000"), + (9e+4, "90000"), + (9e+5, "900000"), + (9e+6, "9000000"), + (9e+7, "90000000"), + (9e+8, "900000000"), + (9e+9, "9000000000"), + (9e+10, "90000000000"), + (9e+11, "900000000000"), + (9e+12, "9000000000000"), + (9e+13, "90000000000000"), + (9e+14, "900000000000000"), + (9e+15, "9000000000000000"), + (9e+16, "90000000000000000"), + (9e+17, "900000000000000000"), + (9e+18, "9000000000000000000"), + (9e+19, "90000000000000000000"), + (9e+20, "900000000000000000000"), + (9e+100, "90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (9e+307, + "90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (9e-1, "0.9"), + (9e-2, "0.09"), + (9e-3, "0.009"), + (9e-4, "0.0009"), + (9e-5, "0.00009"), + (9e-6, "0.000009"), + (9e-7, "0.0000009"), + (9e-8, "0.00000009"), + (9e-9, "0.000000009"), + (9e-10, "0.0000000009"), + (9e-11, "0.00000000009"), + (9e-12, "0.000000000009"), + (9e-13, "0.0000000000009"), + (9e-14, "0.00000000000009"), + (9e-15, "0.000000000000009"), + (9e-16, "0.0000000000000009"), + (9e-17, "0.00000000000000009"), + (9e-18, "0.000000000000000009"), + (9e-19, "0.0000000000000000009"), + (9e-20, "0.00000000000000000009"), + (9e-100, "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009"), + (9e-320, + "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009"), + (-1.0, "-1"), + (-1e+1, "-10"), + (-1e+2, "-100"), + (-1e+3, "-1000"), + (-1e+4, "-10000"), + (-1e+5, "-100000"), + (-1e+6, "-1000000"), + (-1e+7, "-10000000"), + (-1e+8, "-100000000"), + (-1e+9, "-1000000000"), + (-1e+10, "-10000000000"), + (-1e+11, "-100000000000"), + (-1e+12, "-1000000000000"), + (-1e+13, "-10000000000000"), + (-1e+14, "-100000000000000"), + (-1e+15, "-1000000000000000"), + (-1e+16, "-10000000000000000"), + (-1e+17, "-100000000000000000"), + (-1e+18, "-1000000000000000000"), + (-1e+19, "-10000000000000000000"), + (-1e+20, "-100000000000000000000"), + (-1e+100, "-10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-1e+308, + "-100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-1e-1, "-0.1"), + (-1e-2, "-0.01"), + (-1e-3, "-0.001"), + (-1e-4, "-0.0001"), + (-1e-5, "-0.00001"), + (-1e-6, "-0.000001"), + (-1e-7, "-0.0000001"), + (-1e-8, "-0.00000001"), + (-1e-9, "-0.000000001"), + (-1e-10, "-0.0000000001"), + (-1e-11, "-0.00000000001"), + (-1e-12, "-0.000000000001"), + (-1e-13, "-0.0000000000001"), + (-1e-14, "-0.00000000000001"), + (-1e-15, "-0.000000000000001"), + (-1e-16, "-0.0000000000000001"), + (-1e-17, "-0.00000000000000001"), + (-1e-18, "-0.000000000000000001"), + (-1e-19, "-0.0000000000000000001"), + (-1e-20, "-0.00000000000000000001"), + (-1e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), + (-1e-317, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), + (-2.0, "-2"), + (-2e+1, "-20"), + (-2e+2, "-200"), + (-2e+3, "-2000"), + (-2e+4, "-20000"), + (-2e+5, "-200000"), + (-2e+6, "-2000000"), + (-2e+7, "-20000000"), + (-2e+8, "-200000000"), + (-2e+9, "-2000000000"), + (-2e+10, "-20000000000"), + (-2e+11, "-200000000000"), + (-2e+12, "-2000000000000"), + (-2e+13, "-20000000000000"), + (-2e+14, "-200000000000000"), + (-2e+15, "-2000000000000000"), + (-2e+16, "-20000000000000000"), + (-2e+17, "-200000000000000000"), + (-2e+18, "-2000000000000000000"), + (-2e+19, "-20000000000000000000"), + (-2e+20, "-200000000000000000000"), + (-2e+100, "-20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-2e+307, + "-20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-2e-1, "-0.2"), + (-2e-2, "-0.02"), + (-2e-3, "-0.002"), + (-2e-4, "-0.0002"), + (-2e-5, "-0.00002"), + (-2e-6, "-0.000002"), + (-2e-7, "-0.0000002"), + (-2e-8, "-0.00000002"), + (-2e-9, "-0.000000002"), + (-2e-10, "-0.0000000002"), + (-2e-11, "-0.00000000002"), + (-2e-12, "-0.000000000002"), + (-2e-13, "-0.0000000000002"), + (-2e-14, "-0.00000000000002"), + (-2e-15, "-0.000000000000002"), + (-2e-16, "-0.0000000000000002"), + (-2e-17, "-0.00000000000000002"), + (-2e-18, "-0.000000000000000002"), + (-2e-19, "-0.0000000000000000002"), + (-2e-20, "-0.00000000000000000002"), + (-2e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"), + (-2e-320, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"), + (-3.0, "-3"), + (-3e+1, "-30"), + (-3e+2, "-300"), + (-3e+3, "-3000"), + (-3e+4, "-30000"), + (-3e+5, "-300000"), + (-3e+6, "-3000000"), + (-3e+7, "-30000000"), + (-3e+8, "-300000000"), + (-3e+9, "-3000000000"), + (-3e+10, "-30000000000"), + (-3e+11, "-300000000000"), + (-3e+12, "-3000000000000"), + (-3e+13, "-30000000000000"), + (-3e+14, "-300000000000000"), + (-3e+15, "-3000000000000000"), + (-3e+16, "-30000000000000000"), + (-3e+17, "-300000000000000000"), + (-3e+18, "-3000000000000000000"), + (-3e+19, "-30000000000000000000"), + (-3e+20, "-300000000000000000000"), + (-3e+100, "-30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-3e+307, + "-30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-3e-1, "-0.3"), + (-3e-2, "-0.03"), + (-3e-3, "-0.003"), + (-3e-4, "-0.0003"), + (-3e-5, "-0.00003"), + (-3e-6, "-0.000003"), + (-3e-7, "-0.0000003"), + (-3e-8, "-0.00000003"), + (-3e-9, "-0.000000003"), + (-3e-10, "-0.0000000003"), + (-3e-11, "-0.00000000003"), + (-3e-12, "-0.000000000003"), + (-3e-13, "-0.0000000000003"), + (-3e-14, "-0.00000000000003"), + (-3e-15, "-0.000000000000003"), + (-3e-16, "-0.0000000000000003"), + (-3e-17, "-0.00000000000000003"), + (-3e-18, "-0.000000000000000003"), + (-3e-19, "-0.0000000000000000003"), + (-3e-20, "-0.00000000000000000003"), + (-3e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"), + (-3e-320, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"), + (-4.0, "-4"), + (-4e+1, "-40"), + (-4e+2, "-400"), + (-4e+3, "-4000"), + (-4e+4, "-40000"), + (-4e+5, "-400000"), + (-4e+6, "-4000000"), + (-4e+7, "-40000000"), + (-4e+8, "-400000000"), + (-4e+9, "-4000000000"), + (-4e+10, "-40000000000"), + (-4e+11, "-400000000000"), + (-4e+12, "-4000000000000"), + (-4e+13, "-40000000000000"), + (-4e+14, "-400000000000000"), + (-4e+15, "-4000000000000000"), + (-4e+16, "-40000000000000000"), + (-4e+17, "-400000000000000000"), + (-4e+18, "-4000000000000000000"), + (-4e+19, "-40000000000000000000"), + (-4e+20, "-400000000000000000000"), + (-4e+100, "-40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-4e+307, + "-40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-4e-1, "-0.4"), + (-4e-2, "-0.04"), + (-4e-3, "-0.004"), + (-4e-4, "-0.0004"), + (-4e-5, "-0.00004"), + (-4e-6, "-0.000004"), + (-4e-7, "-0.0000004"), + (-4e-8, "-0.00000004"), + (-4e-9, "-0.000000004"), + (-4e-10, "-0.0000000004"), + (-4e-11, "-0.00000000004"), + (-4e-12, "-0.000000000004"), + (-4e-13, "-0.0000000000004"), + (-4e-14, "-0.00000000000004"), + (-4e-15, "-0.000000000000004"), + (-4e-16, "-0.0000000000000004"), + (-4e-17, "-0.00000000000000004"), + (-4e-18, "-0.000000000000000004"), + (-4e-19, "-0.0000000000000000004"), + (-4e-20, "-0.00000000000000000004"), + (-4e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004"), + (-4e-320, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004"), + (-5.0, "-5"), + (-5e+1, "-50"), + (-5e+2, "-500"), + (-5e+3, "-5000"), + (-5e+4, "-50000"), + (-5e+5, "-500000"), + (-5e+6, "-5000000"), + (-5e+7, "-50000000"), + (-5e+8, "-500000000"), + (-5e+9, "-5000000000"), + (-5e+10, "-50000000000"), + (-5e+11, "-500000000000"), + (-5e+12, "-5000000000000"), + (-5e+13, "-50000000000000"), + (-5e+14, "-500000000000000"), + (-5e+15, "-5000000000000000"), + (-5e+16, "-50000000000000000"), + (-5e+17, "-500000000000000000"), + (-5e+18, "-5000000000000000000"), + (-5e+19, "-50000000000000000000"), + (-5e+20, "-500000000000000000000"), + (-5e+100, "-50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-5e+307, + "-50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-5e-1, "-0.5"), + (-5e-2, "-0.05"), + (-5e-3, "-0.005"), + (-5e-4, "-0.0005"), + (-5e-5, "-0.00005"), + (-5e-6, "-0.000005"), + (-5e-7, "-0.0000005"), + (-5e-8, "-0.00000005"), + (-5e-9, "-0.000000005"), + (-5e-10, "-0.0000000005"), + (-5e-11, "-0.00000000005"), + (-5e-12, "-0.000000000005"), + (-5e-13, "-0.0000000000005"), + (-5e-14, "-0.00000000000005"), + (-5e-15, "-0.000000000000005"), + (-5e-16, "-0.0000000000000005"), + (-5e-17, "-0.00000000000000005"), + (-5e-18, "-0.000000000000000005"), + (-5e-19, "-0.0000000000000000005"), + (-5e-20, "-0.00000000000000000005"), + (-5e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"), + (-5e-320, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"), + (-6.0, "-6"), + (-6e+1, "-60"), + (-6e+2, "-600"), + (-6e+3, "-6000"), + (-6e+4, "-60000"), + (-6e+5, "-600000"), + (-6e+6, "-6000000"), + (-6e+7, "-60000000"), + (-6e+8, "-600000000"), + (-6e+9, "-6000000000"), + (-6e+10, "-60000000000"), + (-6e+11, "-600000000000"), + (-6e+12, "-6000000000000"), + (-6e+13, "-60000000000000"), + (-6e+14, "-600000000000000"), + (-6e+15, "-6000000000000000"), + (-6e+16, "-60000000000000000"), + (-6e+17, "-600000000000000000"), + (-6e+18, "-6000000000000000000"), + (-6e+19, "-60000000000000000000"), + (-6e+20, "-600000000000000000000"), + (-6e+100, "-60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-6e+307, + "-60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-6e-1, "-0.6"), + (-6e-2, "-0.06"), + (-6e-3, "-0.006"), + (-6e-4, "-0.0006"), + (-6e-5, "-0.00006"), + (-6e-6, "-0.000006"), + (-6e-7, "-0.0000006"), + (-6e-8, "-0.00000006"), + (-6e-9, "-0.000000006"), + (-6e-10, "-0.0000000006"), + (-6e-11, "-0.00000000006"), + (-6e-12, "-0.000000000006"), + (-6e-13, "-0.0000000000006"), + (-6e-14, "-0.00000000000006"), + (-6e-15, "-0.000000000000006"), + (-6e-16, "-0.0000000000000006"), + (-6e-17, "-0.00000000000000006"), + (-6e-18, "-0.000000000000000006"), + (-6e-19, "-0.0000000000000000006"), + (-6e-20, "-0.00000000000000000006"), + (-6e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006"), + (-6e-320, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006"), + (-7.0, "-7"), + (-7e+1, "-70"), + (-7e+2, "-700"), + (-7e+3, "-7000"), + (-7e+4, "-70000"), + (-7e+5, "-700000"), + (-7e+6, "-7000000"), + (-7e+7, "-70000000"), + (-7e+8, "-700000000"), + (-7e+9, "-7000000000"), + (-7e+10, "-70000000000"), + (-7e+11, "-700000000000"), + (-7e+12, "-7000000000000"), + (-7e+13, "-70000000000000"), + (-7e+14, "-700000000000000"), + (-7e+15, "-7000000000000000"), + (-7e+16, "-70000000000000000"), + (-7e+17, "-700000000000000000"), + (-7e+18, "-7000000000000000000"), + (-7e+19, "-70000000000000000000"), + (-7e+20, "-700000000000000000000"), + (-7e+100, "-70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-7e+307, + "-70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-7e-1, "-0.7"), + (-7e-2, "-0.07"), + (-7e-3, "-0.007"), + (-7e-4, "-0.0007"), + (-7e-5, "-0.00007"), + (-7e-6, "-0.000007"), + (-7e-7, "-0.0000007"), + (-7e-8, "-0.00000007"), + (-7e-9, "-0.000000007"), + (-7e-10, "-0.0000000007"), + (-7e-11, "-0.00000000007"), + (-7e-12, "-0.000000000007"), + (-7e-13, "-0.0000000000007"), + (-7e-14, "-0.00000000000007"), + (-7e-15, "-0.000000000000007"), + (-7e-16, "-0.0000000000000007"), + (-7e-17, "-0.00000000000000007"), + (-7e-18, "-0.000000000000000007"), + (-7e-19, "-0.0000000000000000007"), + (-7e-20, "-0.00000000000000000007"), + (-7e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007"), + (-7e-320, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007"), + (-8.0, "-8"), + (-8e+1, "-80"), + (-8e+2, "-800"), + (-8e+3, "-8000"), + (-8e+4, "-80000"), + (-8e+5, "-800000"), + (-8e+6, "-8000000"), + (-8e+7, "-80000000"), + (-8e+8, "-800000000"), + (-8e+9, "-8000000000"), + (-8e+10, "-80000000000"), + (-8e+11, "-800000000000"), + (-8e+12, "-8000000000000"), + (-8e+13, "-80000000000000"), + (-8e+14, "-800000000000000"), + (-8e+15, "-8000000000000000"), + (-8e+16, "-80000000000000000"), + (-8e+17, "-800000000000000000"), + (-8e+18, "-8000000000000000000"), + (-8e+19, "-80000000000000000000"), + (-8e+20, "-800000000000000000000"), + (-8e+100, "-80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-8e+307, + "-80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-8e-1, "-0.8"), + (-8e-2, "-0.08"), + (-8e-3, "-0.008"), + (-8e-4, "-0.0008"), + (-8e-5, "-0.00008"), + (-8e-6, "-0.000008"), + (-8e-7, "-0.0000008"), + (-8e-8, "-0.00000008"), + (-8e-9, "-0.000000008"), + (-8e-10, "-0.0000000008"), + (-8e-11, "-0.00000000008"), + (-8e-12, "-0.000000000008"), + (-8e-13, "-0.0000000000008"), + (-8e-14, "-0.00000000000008"), + (-8e-15, "-0.000000000000008"), + (-8e-16, "-0.0000000000000008"), + (-8e-17, "-0.00000000000000008"), + (-8e-18, "-0.000000000000000008"), + (-8e-19, "-0.0000000000000000008"), + (-8e-20, "-0.00000000000000000008"), + (-8e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008"), + (-8e-320, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008"), + (-9.0, "-9"), + (-9e+1, "-90"), + (-9e+2, "-900"), + (-9e+3, "-9000"), + (-9e+4, "-90000"), + (-9e+5, "-900000"), + (-9e+6, "-9000000"), + (-9e+7, "-90000000"), + (-9e+8, "-900000000"), + (-9e+9, "-9000000000"), + (-9e+10, "-90000000000"), + (-9e+11, "-900000000000"), + (-9e+12, "-9000000000000"), + (-9e+13, "-90000000000000"), + (-9e+14, "-900000000000000"), + (-9e+15, "-9000000000000000"), + (-9e+16, "-90000000000000000"), + (-9e+17, "-900000000000000000"), + (-9e+18, "-9000000000000000000"), + (-9e+19, "-90000000000000000000"), + (-9e+20, "-900000000000000000000"), + (-9e+100, "-90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-9e+307, + "-90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + (-9e-1, "-0.9"), + (-9e-2, "-0.09"), + (-9e-3, "-0.009"), + (-9e-4, "-0.0009"), + (-9e-5, "-0.00009"), + (-9e-6, "-0.000009"), + (-9e-7, "-0.0000009"), + (-9e-8, "-0.00000009"), + (-9e-9, "-0.000000009"), + (-9e-10, "-0.0000000009"), + (-9e-11, "-0.00000000009"), + (-9e-12, "-0.000000000009"), + (-9e-13, "-0.0000000000009"), + (-9e-14, "-0.00000000000009"), + (-9e-15, "-0.000000000000009"), + (-9e-16, "-0.0000000000000009"), + (-9e-17, "-0.00000000000000009"), + (-9e-18, "-0.000000000000000009"), + (-9e-19, "-0.0000000000000000009"), + (-9e-20, "-0.00000000000000000009"), + (-9e-100, "-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009"), + (-9e-320, + "-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009"), + (1234567890123456.0, "1234567890123456"), + (123456789012345.6, "123456789012345.6"), + (12345678901234.56, "12345678901234.56"), + (1234567890123.456, "1234567890123.456"), + (123456789012.3456, "123456789012.3456"), + (12345678901.23456, "12345678901.23456"), + (1234567890.123456, "1234567890.123456"), + (123456789.0123456, "123456789.0123456"), + (12345678.90123456, "12345678.90123456"), + (1234567.890123456, "1234567.890123456"), + (123456.7890123456, "123456.7890123456"), + (12345.67890123456, "12345.67890123456"), + (1234.567890123456, "1234.567890123456"), + (123.4567890123456, "123.4567890123456"), + (12.34567890123456, "12.34567890123456"), + (1.234567890123456, "1.234567890123456"), + (-1234567890123456.0, "-1234567890123456"), + (-123456789012345.6, "-123456789012345.6"), + (-12345678901234.56, "-12345678901234.56"), + (-1234567890123.456, "-1234567890123.456"), + (-123456789012.3456, "-123456789012.3456"), + (-12345678901.23456, "-12345678901.23456"), + (-1234567890.123456, "-1234567890.123456"), + (-123456789.0123456, "-123456789.0123456"), + (-12345678.90123456, "-12345678.90123456"), + (-1234567.890123456, "-1234567.890123456"), + (-123456.7890123456, "-123456.7890123456"), + (-12345.67890123456, "-12345.67890123456"), + (-1234.567890123456, "-1234.567890123456"), + (-123.4567890123456, "-123.4567890123456"), + (-12.34567890123456, "-12.34567890123456"), + (-1.234567890123456, "-1.234567890123456"), + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach ((double value, string expected) in Data) yield return [value, expected]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalIsDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalIsDataAttribute.cs new file mode 100644 index 0000000..4fc0778 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalIsDataAttribute.cs @@ -0,0 +1,33 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalIsDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal value in TestDataGenerator.GenerateConstantValues()) + yield return [value, Guid.NewGuid()]; + + foreach (decimal value in TestDataGenerator.GenerateScaledValues()) + yield return [value, Guid.NewGuid()]; + + foreach (decimal value in TestDataGenerator.GenerateRandomValues()) + yield return [value, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/BigDecimalWriteDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/BigDecimalWriteDataAttribute.cs new file mode 100644 index 0000000..c7b6849 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/BigDecimalWriteDataAttribute.cs @@ -0,0 +1,27 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class BigDecimalWriteDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal value in TestDataGenerator.GenerateRandomValues()) + yield return [value]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/NumberFormatDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/NumberFormatDataAttribute.cs new file mode 100644 index 0000000..5d34aec --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/NumberFormatDataAttribute.cs @@ -0,0 +1,37 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class NumberFormatDataAttribute : DataAttribute +{ + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (CultureInfo culture in TestDataGenerator.GenerateCultures()) + { + foreach (decimal value in TestDataGenerator.GenerateStaticValues()) + yield return [value, culture, Guid.NewGuid()]; + + foreach (decimal value in TestDataGenerator.GenerateScaledValues()) + yield return [value, culture, Guid.NewGuid()]; + + foreach (decimal value in TestDataGenerator.GenerateRandomValues()) + yield return [value, culture, Guid.NewGuid()]; + } + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/NumberInfoEqualityComparerDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/NumberInfoEqualityComparerDataAttribute.cs new file mode 100644 index 0000000..e1d8280 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/NumberInfoEqualityComparerDataAttribute.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class NumberInfoEqualityComparerDataAttribute : DataAttribute +{ + private static readonly int[] Values = [0, 123456000, -123456000]; + private static readonly int[] Scales = [0, 1, 2, 3, 10]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal left in TestDataGenerator.GenerateScaledValues(Values, Scales)) + foreach (decimal right in TestDataGenerator.GenerateScaledValues(Values, Scales)) + yield return [left, right, Guid.NewGuid()]; + + foreach (decimal left in TestDataGenerator.GenerateRandomValues(count: 10)) + foreach (decimal right in TestDataGenerator.GenerateRandomValues(count: 10)) + yield return [left, right, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/NumberInfoOrdinalityComparerDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/NumberInfoOrdinalityComparerDataAttribute.cs new file mode 100644 index 0000000..851ab30 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/NumberInfoOrdinalityComparerDataAttribute.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class NumberInfoOrdinalityComparerDataAttribute : DataAttribute +{ + private static readonly int[] Values = [0, 123456000, -123456000]; + private static readonly int[] Scales = [0, 1, 2, 3, 10]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach (decimal left in TestDataGenerator.GenerateScaledValues(Values, Scales)) + foreach (decimal right in TestDataGenerator.GenerateScaledValues(Values, Scales)) + yield return [left, right, Guid.NewGuid()]; + + foreach (decimal left in TestDataGenerator.GenerateRandomValues(count: 10)) + foreach (decimal right in TestDataGenerator.GenerateRandomValues(count: 10)) + yield return [left, right, Guid.NewGuid()]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToDecimalDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToDecimalDataAttribute.cs new file mode 100644 index 0000000..7b663d3 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToDecimalDataAttribute.cs @@ -0,0 +1,466 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class NumericsExtensionsToDecimalDataAttribute : DataAttribute +{ + private static readonly (Int128 Value, int Scale, ScaleMode Mode, string Expected)[] Data = + [ + (0, 0, ScaleMode.Integral, "0"), + (0, 1, ScaleMode.Integral, "0.0"), + (0, 2, ScaleMode.Integral, "0.00"), + (0, 3, ScaleMode.Integral, "0.000"), + (0, 4, ScaleMode.Integral, "0.0000"), + (0, 5, ScaleMode.Integral, "0.00000"), + (0, 6, ScaleMode.Integral, "0.000000"), + (0, 7, ScaleMode.Integral, "0.0000000"), + (0, 8, ScaleMode.Integral, "0.00000000"), + (0, 9, ScaleMode.Integral, "0.000000000"), + (0, 10, ScaleMode.Integral, "0.0000000000"), + (0, 28, ScaleMode.Integral, "0.0000000000000000000000000000"), + (1, 0, ScaleMode.Integral, "1"), + (1, 1, ScaleMode.Integral, "1.0"), + (1, 2, ScaleMode.Integral, "1.00"), + (1, 3, ScaleMode.Integral, "1.000"), + (1, 4, ScaleMode.Integral, "1.0000"), + (1, 5, ScaleMode.Integral, "1.00000"), + (1, 6, ScaleMode.Integral, "1.000000"), + (1, 7, ScaleMode.Integral, "1.0000000"), + (1, 8, ScaleMode.Integral, "1.00000000"), + (1, 9, ScaleMode.Integral, "1.000000000"), + (1, 10, ScaleMode.Integral, "1.0000000000"), + (1, 28, ScaleMode.Integral, "1.0000000000000000000000000000"), + (-1, 0, ScaleMode.Integral, "-1"), + (-1, 1, ScaleMode.Integral, "-1.0"), + (-1, 2, ScaleMode.Integral, "-1.00"), + (-1, 3, ScaleMode.Integral, "-1.000"), + (-1, 4, ScaleMode.Integral, "-1.0000"), + (-1, 5, ScaleMode.Integral, "-1.00000"), + (-1, 6, ScaleMode.Integral, "-1.000000"), + (-1, 7, ScaleMode.Integral, "-1.0000000"), + (-1, 8, ScaleMode.Integral, "-1.00000000"), + (-1, 9, ScaleMode.Integral, "-1.000000000"), + (-1, 10, ScaleMode.Integral, "-1.0000000000"), + (-1, 28, ScaleMode.Integral, "-1.0000000000000000000000000000"), + (0, 0, ScaleMode.Fractional, "0"), + (0, 1, ScaleMode.Fractional, "0.0"), + (0, 2, ScaleMode.Fractional, "0.00"), + (0, 3, ScaleMode.Fractional, "0.000"), + (0, 4, ScaleMode.Fractional, "0.0000"), + (0, 5, ScaleMode.Fractional, "0.00000"), + (0, 6, ScaleMode.Fractional, "0.000000"), + (0, 7, ScaleMode.Fractional, "0.0000000"), + (0, 8, ScaleMode.Fractional, "0.00000000"), + (0, 9, ScaleMode.Fractional, "0.000000000"), + (0, 10, ScaleMode.Fractional, "0.0000000000"), + (0, 28, ScaleMode.Fractional, "0.0000000000000000000000000000"), + (1, 0, ScaleMode.Fractional, "1"), + (1, 1, ScaleMode.Fractional, "0.1"), + (1, 2, ScaleMode.Fractional, "0.01"), + (1, 3, ScaleMode.Fractional, "0.001"), + (1, 4, ScaleMode.Fractional, "0.0001"), + (1, 5, ScaleMode.Fractional, "0.00001"), + (1, 6, ScaleMode.Fractional, "0.000001"), + (1, 7, ScaleMode.Fractional, "0.0000001"), + (1, 8, ScaleMode.Fractional, "0.00000001"), + (1, 9, ScaleMode.Fractional, "0.000000001"), + (1, 10, ScaleMode.Fractional, "0.0000000001"), + (1, 28, ScaleMode.Fractional, "0.0000000000000000000000000001"), + (-1, 0, ScaleMode.Fractional, "-1"), + (-1, 1, ScaleMode.Fractional, "-0.1"), + (-1, 2, ScaleMode.Fractional, "-0.01"), + (-1, 3, ScaleMode.Fractional, "-0.001"), + (-1, 4, ScaleMode.Fractional, "-0.0001"), + (-1, 5, ScaleMode.Fractional, "-0.00001"), + (-1, 6, ScaleMode.Fractional, "-0.000001"), + (-1, 7, ScaleMode.Fractional, "-0.0000001"), + (-1, 8, ScaleMode.Fractional, "-0.00000001"), + (-1, 9, ScaleMode.Fractional, "-0.000000001"), + (-1, 28, ScaleMode.Fractional, "-0.0000000000000000000000000001"), + (sbyte.MinValue, 0, ScaleMode.Integral, "-128"), + (sbyte.MinValue, 1, ScaleMode.Integral, "-128.0"), + (sbyte.MinValue, 2, ScaleMode.Integral, "-128.00"), + (sbyte.MinValue, 3, ScaleMode.Integral, "-128.000"), + (sbyte.MinValue, 4, ScaleMode.Integral, "-128.0000"), + (sbyte.MinValue, 5, ScaleMode.Integral, "-128.00000"), + (sbyte.MinValue, 6, ScaleMode.Integral, "-128.000000"), + (sbyte.MinValue, 7, ScaleMode.Integral, "-128.0000000"), + (sbyte.MinValue, 8, ScaleMode.Integral, "-128.00000000"), + (sbyte.MinValue, 9, ScaleMode.Integral, "-128.000000000"), + (sbyte.MinValue, 10, ScaleMode.Integral, "-128.0000000000"), + (sbyte.MaxValue, 0, ScaleMode.Integral, "127"), + (sbyte.MaxValue, 1, ScaleMode.Integral, "127.0"), + (sbyte.MaxValue, 2, ScaleMode.Integral, "127.00"), + (sbyte.MaxValue, 3, ScaleMode.Integral, "127.000"), + (sbyte.MaxValue, 4, ScaleMode.Integral, "127.0000"), + (sbyte.MaxValue, 5, ScaleMode.Integral, "127.00000"), + (sbyte.MaxValue, 6, ScaleMode.Integral, "127.000000"), + (sbyte.MaxValue, 7, ScaleMode.Integral, "127.0000000"), + (sbyte.MaxValue, 8, ScaleMode.Integral, "127.00000000"), + (sbyte.MaxValue, 9, ScaleMode.Integral, "127.000000000"), + (sbyte.MaxValue, 10, ScaleMode.Integral, "127.0000000000"), + (sbyte.MinValue, 0, ScaleMode.Fractional, "-128"), + (sbyte.MinValue, 1, ScaleMode.Fractional, "-12.8"), + (sbyte.MinValue, 2, ScaleMode.Fractional, "-1.28"), + (sbyte.MinValue, 3, ScaleMode.Fractional, "-0.128"), + (sbyte.MinValue, 4, ScaleMode.Fractional, "-0.0128"), + (sbyte.MinValue, 5, ScaleMode.Fractional, "-0.00128"), + (sbyte.MinValue, 6, ScaleMode.Fractional, "-0.000128"), + (sbyte.MinValue, 7, ScaleMode.Fractional, "-0.0000128"), + (sbyte.MinValue, 8, ScaleMode.Fractional, "-0.00000128"), + (sbyte.MinValue, 9, ScaleMode.Fractional, "-0.000000128"), + (sbyte.MinValue, 10, ScaleMode.Fractional, "-0.0000000128"), + (sbyte.MinValue, 28, ScaleMode.Fractional, "-0.0000000000000000000000000128"), + (sbyte.MaxValue, 0, ScaleMode.Fractional, "127"), + (sbyte.MaxValue, 1, ScaleMode.Fractional, "12.7"), + (sbyte.MaxValue, 2, ScaleMode.Fractional, "1.27"), + (sbyte.MaxValue, 3, ScaleMode.Fractional, "0.127"), + (sbyte.MaxValue, 4, ScaleMode.Fractional, "0.0127"), + (sbyte.MaxValue, 5, ScaleMode.Fractional, "0.00127"), + (sbyte.MaxValue, 6, ScaleMode.Fractional, "0.000127"), + (sbyte.MaxValue, 7, ScaleMode.Fractional, "0.0000127"), + (sbyte.MaxValue, 8, ScaleMode.Fractional, "0.00000127"), + (sbyte.MaxValue, 9, ScaleMode.Fractional, "0.000000127"), + (sbyte.MaxValue, 10, ScaleMode.Fractional, "0.0000000127"), + (sbyte.MaxValue, 28, ScaleMode.Fractional, "0.0000000000000000000000000127"), + (byte.MinValue, 0, ScaleMode.Integral, "0"), + (byte.MinValue, 1, ScaleMode.Integral, "0.0"), + (byte.MinValue, 2, ScaleMode.Integral, "0.00"), + (byte.MinValue, 3, ScaleMode.Integral, "0.000"), + (byte.MinValue, 4, ScaleMode.Integral, "0.0000"), + (byte.MinValue, 5, ScaleMode.Integral, "0.00000"), + (byte.MinValue, 6, ScaleMode.Integral, "0.000000"), + (byte.MinValue, 7, ScaleMode.Integral, "0.0000000"), + (byte.MinValue, 8, ScaleMode.Integral, "0.00000000"), + (byte.MinValue, 9, ScaleMode.Integral, "0.000000000"), + (byte.MinValue, 10, ScaleMode.Integral, "0.0000000000"), + (byte.MinValue, 28, ScaleMode.Integral, "0.0000000000000000000000000000"), + (byte.MaxValue, 0, ScaleMode.Integral, "255"), + (byte.MaxValue, 1, ScaleMode.Integral, "255.0"), + (byte.MaxValue, 2, ScaleMode.Integral, "255.00"), + (byte.MaxValue, 3, ScaleMode.Integral, "255.000"), + (byte.MaxValue, 4, ScaleMode.Integral, "255.0000"), + (byte.MaxValue, 5, ScaleMode.Integral, "255.00000"), + (byte.MaxValue, 6, ScaleMode.Integral, "255.000000"), + (byte.MaxValue, 7, ScaleMode.Integral, "255.0000000"), + (byte.MaxValue, 8, ScaleMode.Integral, "255.00000000"), + (byte.MaxValue, 9, ScaleMode.Integral, "255.000000000"), + (byte.MaxValue, 10, ScaleMode.Integral, "255.0000000000"), + (byte.MinValue, 0, ScaleMode.Fractional, "0"), + (byte.MinValue, 1, ScaleMode.Fractional, "0.0"), + (byte.MinValue, 2, ScaleMode.Fractional, "0.00"), + (byte.MinValue, 3, ScaleMode.Fractional, "0.000"), + (byte.MinValue, 4, ScaleMode.Fractional, "0.0000"), + (byte.MinValue, 5, ScaleMode.Fractional, "0.00000"), + (byte.MinValue, 6, ScaleMode.Fractional, "0.000000"), + (byte.MinValue, 7, ScaleMode.Fractional, "0.0000000"), + (byte.MinValue, 8, ScaleMode.Fractional, "0.00000000"), + (byte.MinValue, 9, ScaleMode.Fractional, "0.000000000"), + (byte.MinValue, 10, ScaleMode.Fractional, "0.0000000000"), + (byte.MinValue, 28, ScaleMode.Fractional, "0.0000000000000000000000000000"), + (byte.MaxValue, 0, ScaleMode.Fractional, "255"), + (byte.MaxValue, 1, ScaleMode.Fractional, "25.5"), + (byte.MaxValue, 2, ScaleMode.Fractional, "2.55"), + (byte.MaxValue, 3, ScaleMode.Fractional, "0.255"), + (byte.MaxValue, 4, ScaleMode.Fractional, "0.0255"), + (byte.MaxValue, 5, ScaleMode.Fractional, "0.00255"), + (byte.MaxValue, 6, ScaleMode.Fractional, "0.000255"), + (byte.MaxValue, 7, ScaleMode.Fractional, "0.0000255"), + (byte.MaxValue, 8, ScaleMode.Fractional, "0.00000255"), + (byte.MaxValue, 9, ScaleMode.Fractional, "0.000000255"), + (byte.MaxValue, 10, ScaleMode.Fractional, "0.0000000255"), + (byte.MaxValue, 28, ScaleMode.Fractional, "0.0000000000000000000000000255"), + (short.MinValue, 0, ScaleMode.Integral, "-32768"), + (short.MinValue, 1, ScaleMode.Integral, "-32768.0"), + (short.MinValue, 2, ScaleMode.Integral, "-32768.00"), + (short.MinValue, 3, ScaleMode.Integral, "-32768.000"), + (short.MinValue, 4, ScaleMode.Integral, "-32768.0000"), + (short.MinValue, 5, ScaleMode.Integral, "-32768.00000"), + (short.MinValue, 6, ScaleMode.Integral, "-32768.000000"), + (short.MinValue, 7, ScaleMode.Integral, "-32768.0000000"), + (short.MinValue, 8, ScaleMode.Integral, "-32768.00000000"), + (short.MinValue, 9, ScaleMode.Integral, "-32768.000000000"), + (short.MinValue, 10, ScaleMode.Integral, "-32768.0000000000"), + (short.MaxValue, 0, ScaleMode.Integral, "32767"), + (short.MaxValue, 1, ScaleMode.Integral, "32767.0"), + (short.MaxValue, 2, ScaleMode.Integral, "32767.00"), + (short.MaxValue, 3, ScaleMode.Integral, "32767.000"), + (short.MaxValue, 4, ScaleMode.Integral, "32767.0000"), + (short.MaxValue, 5, ScaleMode.Integral, "32767.00000"), + (short.MaxValue, 6, ScaleMode.Integral, "32767.000000"), + (short.MaxValue, 7, ScaleMode.Integral, "32767.0000000"), + (short.MaxValue, 8, ScaleMode.Integral, "32767.00000000"), + (short.MaxValue, 9, ScaleMode.Integral, "32767.000000000"), + (short.MaxValue, 10, ScaleMode.Integral, "32767.0000000000"), + (short.MinValue, 0, ScaleMode.Fractional, "-32768"), + (short.MinValue, 1, ScaleMode.Fractional, "-3276.8"), + (short.MinValue, 2, ScaleMode.Fractional, "-327.68"), + (short.MinValue, 3, ScaleMode.Fractional, "-32.768"), + (short.MinValue, 4, ScaleMode.Fractional, "-3.2768"), + (short.MinValue, 5, ScaleMode.Fractional, "-0.32768"), + (short.MinValue, 6, ScaleMode.Fractional, "-0.032768"), + (short.MinValue, 7, ScaleMode.Fractional, "-0.0032768"), + (short.MinValue, 8, ScaleMode.Fractional, "-0.00032768"), + (short.MinValue, 9, ScaleMode.Fractional, "-0.000032768"), + (short.MinValue, 10, ScaleMode.Fractional, "-0.0000032768"), + (short.MinValue, 28, ScaleMode.Fractional, "-0.0000000000000000000000032768"), + (short.MaxValue, 0, ScaleMode.Fractional, "32767"), + (short.MaxValue, 1, ScaleMode.Fractional, "3276.7"), + (short.MaxValue, 2, ScaleMode.Fractional, "327.67"), + (short.MaxValue, 3, ScaleMode.Fractional, "32.767"), + (short.MaxValue, 4, ScaleMode.Fractional, "3.2767"), + (short.MaxValue, 5, ScaleMode.Fractional, "0.32767"), + (short.MaxValue, 6, ScaleMode.Fractional, "0.032767"), + (short.MaxValue, 7, ScaleMode.Fractional, "0.0032767"), + (short.MaxValue, 8, ScaleMode.Fractional, "0.00032767"), + (short.MaxValue, 9, ScaleMode.Fractional, "0.000032767"), + (short.MaxValue, 10, ScaleMode.Fractional, "0.0000032767"), + (short.MaxValue, 28, ScaleMode.Fractional, "0.0000000000000000000000032767"), + (ushort.MinValue, 0, ScaleMode.Integral, "0"), + (ushort.MinValue, 1, ScaleMode.Integral, "0.0"), + (ushort.MinValue, 2, ScaleMode.Integral, "0.00"), + (ushort.MinValue, 3, ScaleMode.Integral, "0.000"), + (ushort.MinValue, 4, ScaleMode.Integral, "0.0000"), + (ushort.MinValue, 5, ScaleMode.Integral, "0.00000"), + (ushort.MinValue, 6, ScaleMode.Integral, "0.000000"), + (ushort.MinValue, 7, ScaleMode.Integral, "0.0000000"), + (ushort.MinValue, 8, ScaleMode.Integral, "0.00000000"), + (ushort.MinValue, 9, ScaleMode.Integral, "0.000000000"), + (ushort.MinValue, 10, ScaleMode.Integral, "0.0000000000"), + (ushort.MaxValue, 0, ScaleMode.Integral, "65535"), + (ushort.MaxValue, 1, ScaleMode.Integral, "65535.0"), + (ushort.MaxValue, 2, ScaleMode.Integral, "65535.00"), + (ushort.MaxValue, 3, ScaleMode.Integral, "65535.000"), + (ushort.MaxValue, 4, ScaleMode.Integral, "65535.0000"), + (ushort.MaxValue, 5, ScaleMode.Integral, "65535.00000"), + (ushort.MaxValue, 6, ScaleMode.Integral, "65535.000000"), + (ushort.MaxValue, 7, ScaleMode.Integral, "65535.0000000"), + (ushort.MaxValue, 8, ScaleMode.Integral, "65535.00000000"), + (ushort.MaxValue, 9, ScaleMode.Integral, "65535.000000000"), + (ushort.MaxValue, 10, ScaleMode.Integral, "65535.0000000000"), + (ushort.MinValue, 0, ScaleMode.Fractional, "0"), + (ushort.MinValue, 1, ScaleMode.Fractional, "0.0"), + (ushort.MinValue, 2, ScaleMode.Fractional, "0.00"), + (ushort.MinValue, 3, ScaleMode.Fractional, "0.000"), + (ushort.MinValue, 4, ScaleMode.Fractional, "0.0000"), + (ushort.MinValue, 5, ScaleMode.Fractional, "0.00000"), + (ushort.MinValue, 6, ScaleMode.Fractional, "0.000000"), + (ushort.MinValue, 7, ScaleMode.Fractional, "0.0000000"), + (ushort.MinValue, 8, ScaleMode.Fractional, "0.00000000"), + (ushort.MinValue, 9, ScaleMode.Fractional, "0.000000000"), + (ushort.MinValue, 10, ScaleMode.Fractional, "0.0000000000"), + (ushort.MinValue, 28, ScaleMode.Fractional, "0.0000000000000000000000000000"), + (ushort.MaxValue, 0, ScaleMode.Fractional, "65535"), + (ushort.MaxValue, 1, ScaleMode.Fractional, "6553.5"), + (ushort.MaxValue, 2, ScaleMode.Fractional, "655.35"), + (ushort.MaxValue, 3, ScaleMode.Fractional, "65.535"), + (ushort.MaxValue, 4, ScaleMode.Fractional, "6.5535"), + (ushort.MaxValue, 5, ScaleMode.Fractional, "0.65535"), + (ushort.MaxValue, 6, ScaleMode.Fractional, "0.065535"), + (ushort.MaxValue, 7, ScaleMode.Fractional, "0.0065535"), + (ushort.MaxValue, 8, ScaleMode.Fractional, "0.00065535"), + (ushort.MaxValue, 9, ScaleMode.Fractional, "0.000065535"), + (ushort.MaxValue, 10, ScaleMode.Fractional, "0.0000065535"), + (ushort.MaxValue, 28, ScaleMode.Fractional, "0.0000000000000000000000065535"), + (int.MinValue, 0, ScaleMode.Integral, "-2147483648"), + (int.MinValue, 1, ScaleMode.Integral, "-2147483648.0"), + (int.MinValue, 2, ScaleMode.Integral, "-2147483648.00"), + (int.MinValue, 3, ScaleMode.Integral, "-2147483648.000"), + (int.MinValue, 4, ScaleMode.Integral, "-2147483648.0000"), + (int.MinValue, 5, ScaleMode.Integral, "-2147483648.00000"), + (int.MinValue, 6, ScaleMode.Integral, "-2147483648.000000"), + (int.MinValue, 7, ScaleMode.Integral, "-2147483648.0000000"), + (int.MinValue, 8, ScaleMode.Integral, "-2147483648.00000000"), + (int.MinValue, 9, ScaleMode.Integral, "-2147483648.000000000"), + (int.MinValue, 10, ScaleMode.Integral, "-2147483648.0000000000"), + (int.MaxValue, 0, ScaleMode.Integral, "2147483647"), + (int.MaxValue, 1, ScaleMode.Integral, "2147483647.0"), + (int.MaxValue, 2, ScaleMode.Integral, "2147483647.00"), + (int.MaxValue, 3, ScaleMode.Integral, "2147483647.000"), + (int.MaxValue, 4, ScaleMode.Integral, "2147483647.0000"), + (int.MaxValue, 5, ScaleMode.Integral, "2147483647.00000"), + (int.MaxValue, 6, ScaleMode.Integral, "2147483647.000000"), + (int.MaxValue, 7, ScaleMode.Integral, "2147483647.0000000"), + (int.MaxValue, 8, ScaleMode.Integral, "2147483647.00000000"), + (int.MaxValue, 9, ScaleMode.Integral, "2147483647.000000000"), + (int.MaxValue, 10, ScaleMode.Integral, "2147483647.0000000000"), + (int.MinValue, 0, ScaleMode.Fractional, "-2147483648"), + (int.MinValue, 1, ScaleMode.Fractional, "-214748364.8"), + (int.MinValue, 2, ScaleMode.Fractional, "-21474836.48"), + (int.MinValue, 3, ScaleMode.Fractional, "-2147483.648"), + (int.MinValue, 4, ScaleMode.Fractional, "-214748.3648"), + (int.MinValue, 5, ScaleMode.Fractional, "-21474.83648"), + (int.MinValue, 6, ScaleMode.Fractional, "-2147.483648"), + (int.MinValue, 7, ScaleMode.Fractional, "-214.7483648"), + (int.MinValue, 8, ScaleMode.Fractional, "-21.47483648"), + (int.MinValue, 9, ScaleMode.Fractional, "-2.147483648"), + (int.MinValue, 10, ScaleMode.Fractional, "-0.2147483648"), + (int.MinValue, 28, ScaleMode.Fractional, "-0.0000000000000000002147483648"), + (int.MaxValue, 0, ScaleMode.Fractional, "2147483647"), + (int.MaxValue, 1, ScaleMode.Fractional, "214748364.7"), + (int.MaxValue, 2, ScaleMode.Fractional, "21474836.47"), + (int.MaxValue, 3, ScaleMode.Fractional, "2147483.647"), + (int.MaxValue, 4, ScaleMode.Fractional, "214748.3647"), + (int.MaxValue, 5, ScaleMode.Fractional, "21474.83647"), + (int.MaxValue, 6, ScaleMode.Fractional, "2147.483647"), + (int.MaxValue, 7, ScaleMode.Fractional, "214.7483647"), + (int.MaxValue, 8, ScaleMode.Fractional, "21.47483647"), + (int.MaxValue, 9, ScaleMode.Fractional, "2.147483647"), + (int.MaxValue, 10, ScaleMode.Fractional, "0.2147483647"), + (int.MaxValue, 28, ScaleMode.Fractional, "0.0000000000000000002147483647"), + (uint.MinValue, 0, ScaleMode.Integral, "0"), + (uint.MinValue, 1, ScaleMode.Integral, "0.0"), + (uint.MinValue, 2, ScaleMode.Integral, "0.00"), + (uint.MinValue, 3, ScaleMode.Integral, "0.000"), + (uint.MinValue, 4, ScaleMode.Integral, "0.0000"), + (uint.MinValue, 5, ScaleMode.Integral, "0.00000"), + (uint.MinValue, 6, ScaleMode.Integral, "0.000000"), + (uint.MinValue, 7, ScaleMode.Integral, "0.0000000"), + (uint.MinValue, 8, ScaleMode.Integral, "0.00000000"), + (uint.MinValue, 9, ScaleMode.Integral, "0.000000000"), + (uint.MinValue, 10, ScaleMode.Integral, "0.0000000000"), + (uint.MaxValue, 0, ScaleMode.Integral, "4294967295"), + (uint.MaxValue, 1, ScaleMode.Integral, "4294967295.0"), + (uint.MaxValue, 2, ScaleMode.Integral, "4294967295.00"), + (uint.MaxValue, 3, ScaleMode.Integral, "4294967295.000"), + (uint.MaxValue, 4, ScaleMode.Integral, "4294967295.0000"), + (uint.MaxValue, 5, ScaleMode.Integral, "4294967295.00000"), + (uint.MaxValue, 6, ScaleMode.Integral, "4294967295.000000"), + (uint.MaxValue, 7, ScaleMode.Integral, "4294967295.0000000"), + (uint.MaxValue, 8, ScaleMode.Integral, "4294967295.00000000"), + (uint.MaxValue, 9, ScaleMode.Integral, "4294967295.000000000"), + (uint.MaxValue, 10, ScaleMode.Integral, "4294967295.0000000000"), + (uint.MinValue, 0, ScaleMode.Fractional, "0"), + (uint.MinValue, 1, ScaleMode.Fractional, "0.0"), + (uint.MinValue, 2, ScaleMode.Fractional, "0.00"), + (uint.MinValue, 3, ScaleMode.Fractional, "0.000"), + (uint.MinValue, 4, ScaleMode.Fractional, "0.0000"), + (uint.MinValue, 5, ScaleMode.Fractional, "0.00000"), + (uint.MinValue, 6, ScaleMode.Fractional, "0.000000"), + (uint.MinValue, 7, ScaleMode.Fractional, "0.0000000"), + (uint.MinValue, 8, ScaleMode.Fractional, "0.00000000"), + (uint.MinValue, 9, ScaleMode.Fractional, "0.000000000"), + (uint.MinValue, 10, ScaleMode.Fractional, "0.0000000000"), + (uint.MinValue, 28, ScaleMode.Fractional, "0.0000000000000000000000000000"), + (uint.MaxValue, 0, ScaleMode.Fractional, "4294967295"), + (uint.MaxValue, 1, ScaleMode.Fractional, "429496729.5"), + (uint.MaxValue, 2, ScaleMode.Fractional, "42949672.95"), + (uint.MaxValue, 3, ScaleMode.Fractional, "4294967.295"), + (uint.MaxValue, 4, ScaleMode.Fractional, "429496.7295"), + (uint.MaxValue, 5, ScaleMode.Fractional, "42949.67295"), + (uint.MaxValue, 6, ScaleMode.Fractional, "4294.967295"), + (uint.MaxValue, 7, ScaleMode.Fractional, "429.4967295"), + (uint.MaxValue, 8, ScaleMode.Fractional, "42.94967295"), + (uint.MaxValue, 9, ScaleMode.Fractional, "4.294967295"), + (uint.MaxValue, 10, ScaleMode.Fractional, "0.4294967295"), + (uint.MaxValue, 28, ScaleMode.Fractional, "0.0000000000000000004294967295"), + (long.MinValue, 0, ScaleMode.Integral, "-9223372036854775808"), + (long.MinValue, 1, ScaleMode.Integral, "-9223372036854775808.0"), + (long.MinValue, 2, ScaleMode.Integral, "-9223372036854775808.00"), + (long.MinValue, 3, ScaleMode.Integral, "-9223372036854775808.000"), + (long.MinValue, 4, ScaleMode.Integral, "-9223372036854775808.0000"), + (long.MinValue, 5, ScaleMode.Integral, "-9223372036854775808.00000"), + (long.MinValue, 6, ScaleMode.Integral, "-9223372036854775808.000000"), + (long.MinValue, 7, ScaleMode.Integral, "-9223372036854775808.0000000"), + (long.MinValue, 8, ScaleMode.Integral, "-9223372036854775808.00000000"), + (long.MinValue, 9, ScaleMode.Integral, "-9223372036854775808.000000000"), + (long.MaxValue, 0, ScaleMode.Integral, "9223372036854775807"), + (long.MaxValue, 1, ScaleMode.Integral, "9223372036854775807.0"), + (long.MaxValue, 2, ScaleMode.Integral, "9223372036854775807.00"), + (long.MaxValue, 3, ScaleMode.Integral, "9223372036854775807.000"), + (long.MaxValue, 4, ScaleMode.Integral, "9223372036854775807.0000"), + (long.MaxValue, 5, ScaleMode.Integral, "9223372036854775807.00000"), + (long.MaxValue, 6, ScaleMode.Integral, "9223372036854775807.000000"), + (long.MaxValue, 7, ScaleMode.Integral, "9223372036854775807.0000000"), + (long.MaxValue, 8, ScaleMode.Integral, "9223372036854775807.00000000"), + (long.MaxValue, 9, ScaleMode.Integral, "9223372036854775807.000000000"), + (long.MinValue, 0, ScaleMode.Fractional, "-9223372036854775808"), + (long.MinValue, 1, ScaleMode.Fractional, "-922337203685477580.8"), + (long.MinValue, 2, ScaleMode.Fractional, "-92233720368547758.08"), + (long.MinValue, 3, ScaleMode.Fractional, "-9223372036854775.808"), + (long.MinValue, 4, ScaleMode.Fractional, "-922337203685477.5808"), + (long.MinValue, 5, ScaleMode.Fractional, "-92233720368547.75808"), + (long.MinValue, 6, ScaleMode.Fractional, "-9223372036854.775808"), + (long.MinValue, 7, ScaleMode.Fractional, "-922337203685.4775808"), + (long.MinValue, 8, ScaleMode.Fractional, "-92233720368.54775808"), + (long.MinValue, 9, ScaleMode.Fractional, "-9223372036.854775808"), + (long.MinValue, 10, ScaleMode.Fractional, "-922337203.6854775808"), + (long.MinValue, 28, ScaleMode.Fractional, "-0.0000000009223372036854775808"), + (long.MaxValue, 0, ScaleMode.Fractional, "9223372036854775807"), + (long.MaxValue, 1, ScaleMode.Fractional, "922337203685477580.7"), + (long.MaxValue, 2, ScaleMode.Fractional, "92233720368547758.07"), + (long.MaxValue, 3, ScaleMode.Fractional, "9223372036854775.807"), + (long.MaxValue, 4, ScaleMode.Fractional, "922337203685477.5807"), + (long.MaxValue, 5, ScaleMode.Fractional, "92233720368547.75807"), + (long.MaxValue, 6, ScaleMode.Fractional, "9223372036854.775807"), + (long.MaxValue, 7, ScaleMode.Fractional, "922337203685.4775807"), + (long.MaxValue, 8, ScaleMode.Fractional, "92233720368.54775807"), + (long.MaxValue, 9, ScaleMode.Fractional, "9223372036.854775807"), + (long.MaxValue, 10, ScaleMode.Fractional, "922337203.6854775807"), + (long.MaxValue, 28, ScaleMode.Fractional, "0.0000000009223372036854775807"), + (ulong.MinValue, 0, ScaleMode.Integral, "0"), + (ulong.MinValue, 1, ScaleMode.Integral, "0.0"), + (ulong.MinValue, 2, ScaleMode.Integral, "0.00"), + (ulong.MinValue, 3, ScaleMode.Integral, "0.000"), + (ulong.MinValue, 4, ScaleMode.Integral, "0.0000"), + (ulong.MinValue, 5, ScaleMode.Integral, "0.00000"), + (ulong.MinValue, 6, ScaleMode.Integral, "0.000000"), + (ulong.MinValue, 7, ScaleMode.Integral, "0.0000000"), + (ulong.MinValue, 8, ScaleMode.Integral, "0.00000000"), + (ulong.MinValue, 9, ScaleMode.Integral, "0.000000000"), + (ulong.MaxValue, 0, ScaleMode.Integral, "18446744073709551615"), + (ulong.MaxValue, 1, ScaleMode.Integral, "18446744073709551615.0"), + (ulong.MaxValue, 2, ScaleMode.Integral, "18446744073709551615.00"), + (ulong.MaxValue, 3, ScaleMode.Integral, "18446744073709551615.000"), + (ulong.MaxValue, 4, ScaleMode.Integral, "18446744073709551615.0000"), + (ulong.MaxValue, 5, ScaleMode.Integral, "18446744073709551615.00000"), + (ulong.MaxValue, 6, ScaleMode.Integral, "18446744073709551615.000000"), + (ulong.MaxValue, 7, ScaleMode.Integral, "18446744073709551615.0000000"), + (ulong.MaxValue, 8, ScaleMode.Integral, "18446744073709551615.00000000"), + (ulong.MaxValue, 9, ScaleMode.Integral, "18446744073709551615.000000000"), + (ulong.MinValue, 0, ScaleMode.Fractional, "0"), + (ulong.MinValue, 1, ScaleMode.Fractional, "0.0"), + (ulong.MinValue, 2, ScaleMode.Fractional, "0.00"), + (ulong.MinValue, 3, ScaleMode.Fractional, "0.000"), + (ulong.MinValue, 4, ScaleMode.Fractional, "0.0000"), + (ulong.MinValue, 5, ScaleMode.Fractional, "0.00000"), + (ulong.MinValue, 6, ScaleMode.Fractional, "0.000000"), + (ulong.MinValue, 7, ScaleMode.Fractional, "0.0000000"), + (ulong.MinValue, 8, ScaleMode.Fractional, "0.00000000"), + (ulong.MinValue, 9, ScaleMode.Fractional, "0.000000000"), + (ulong.MinValue, 10, ScaleMode.Fractional, "0.0000000000"), + (ulong.MinValue, 28, ScaleMode.Fractional, "0.0000000000000000000000000000"), + (ulong.MaxValue, 0, ScaleMode.Fractional, "18446744073709551615"), + (ulong.MaxValue, 1, ScaleMode.Fractional, "1844674407370955161.5"), + (ulong.MaxValue, 2, ScaleMode.Fractional, "184467440737095516.15"), + (ulong.MaxValue, 3, ScaleMode.Fractional, "18446744073709551.615"), + (ulong.MaxValue, 4, ScaleMode.Fractional, "1844674407370955.1615"), + (ulong.MaxValue, 5, ScaleMode.Fractional, "184467440737095.51615"), + (ulong.MaxValue, 6, ScaleMode.Fractional, "18446744073709.551615"), + (ulong.MaxValue, 7, ScaleMode.Fractional, "1844674407370.9551615"), + (ulong.MaxValue, 8, ScaleMode.Fractional, "184467440737.09551615"), + (ulong.MaxValue, 9, ScaleMode.Fractional, "18446744073.709551615"), + (ulong.MaxValue, 10, ScaleMode.Fractional, "1844674407.3709551615"), + (ulong.MaxValue, 28, ScaleMode.Fractional, "0.0000000018446744073709551615") + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach ((Int128 value, int scale, ScaleMode mode, string expected) in Data) yield return [value, scale, mode, expected]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoDecimalDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoDecimalDataAttribute.cs new file mode 100644 index 0000000..d1c4518 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoDecimalDataAttribute.cs @@ -0,0 +1,133 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class NumericsExtensionsToNumberInfoDecimalDataAttribute : DataAttribute +{ + private static readonly (decimal Value, BigInteger UnscaledValue, int Scale, BigInteger Significand, int Exponent, int Sign, int Precision)[] Data = + [ + (decimal.Zero, 0, 0, 0, 0, 0, 1), + (decimal.MaxValue, (BigInteger)79228162514264337593543950335m, 0, (BigInteger)79228162514264337593543950335m, 0, 1, 29), + (decimal.MinValue, (BigInteger)(-79228162514264337593543950335m), 0, (BigInteger)(-79228162514264337593543950335m), 0, -1, 29), + (1.0m, 10, 1, 1, 0, 1, 2), + (1.00m, 100, 2, 1, 0, 1, 3), + (1.000m, 1000, 3, 1, 0, 1, 4), + (1.0000m, 10000, 4, 1, 0, 1, 5), + (1.00000m, 100000, 5, 1, 0, 1, 6), + (1.000000m, 1000000, 6, 1, 0, 1, 7), + (1.0000000m, 10000000, 7, 1, 0, 1, 8), + (1.00000000m, 100000000, 8, 1, 0, 1, 9), + (1.000000000m, 1000000000, 9, 1, 0, 1, 10), + (1.0000000000m, 10000000000, 10, 1, 0, 1, 11), + (0.1m, 1, 1, 1, -1, 1, 2), + (0.01m, 1, 2, 1, -2, 1, 3), + (0.001m, 1, 3, 1, -3, 1, 4), + (0.0001m, 1, 4, 1, -4, 1, 5), + (0.00001m, 1, 5, 1, -5, 1, 6), + (0.000001m, 1, 6, 1, -6, 1, 7), + (0.0000001m, 1, 7, 1, -7, 1, 8), + (0.00000001m, 1, 8, 1, -8, 1, 9), + (0.000000001m, 1, 9, 1, -9, 1, 10), + (0.0000000001m, 1, 10, 1, -10, 1, 11), + (12345678.9m, 123456789, 1, 123456789, -1, 1, 9), + (1234567.89m, 123456789, 2, 123456789, -2, 1, 9), + (123456.789m, 123456789, 3, 123456789, -3, 1, 9), + (12345.6789m, 123456789, 4, 123456789, -4, 1, 9), + (1234.56789m, 123456789, 5, 123456789, -5, 1, 9), + (123.456789m, 123456789, 6, 123456789, -6, 1, 9), + (12.3456789m, 123456789, 7, 123456789, -7, 1, 9), + (1.23456789m, 123456789, 8, 123456789, -8, 1, 9), + (0.123456789m, 123456789, 9, 123456789, -9, 1, 10), + (0.0123456789m, 123456789, 10, 123456789, -10, 1, 11), + (0.00123456789m, 123456789, 11, 123456789, -11, 1, 12), + (0.000123456789m, 123456789, 12, 123456789, -12, 1, 13), + (0.0000123456789m, 123456789, 13, 123456789, -13, 1, 14), + (0.00000123456789m, 123456789, 14, 123456789, -14, 1, 15), + (0.000000123456789m, 123456789, 15, 123456789, -15, 1, 16), + (0.0000000123456789m, 123456789, 16, 123456789, -16, 1, 17), + (0.00000000123456789m, 123456789, 17, 123456789, -17, 1, 18), + (0.000000000123456789m, 123456789, 18, 123456789, -18, 1, 19), + (123.456m, 123456, 3, 123456, -3, 1, 6), + (123.4560m, 1234560, 4, 123456, -3, 1, 7), + (123.45600m, 12345600, 5, 123456, -3, 1, 8), + (123.456000m, 123456000, 6, 123456, -3, 1, 9), + (123.4560000m, 1234560000, 7, 123456, -3, 1, 10), + (123.45600000m, 12345600000, 8, 123456, -3, 1, 11), + (123.456000000m, 123456000000, 9, 123456, -3, 1, 12), + (123.4560000000m, 1234560000000, 10, 123456, -3, 1, 13), + (123.45600000000m, 12345600000000, 11, 123456, -3, 1, 14), + (123.456000000000m, 123456000000000, 12, 123456, -3, 1, 15), + (123.4560000000000m, 1234560000000000, 13, 123456, -3, 1, 16), + (-1.0m, -10, 1, -1, 0, -1, 2), + (-1.00m, -100, 2, -1, 0, -1, 3), + (-1.000m, -1000, 3, -1, 0, -1, 4), + (-1.0000m, -10000, 4, -1, 0, -1, 5), + (-1.00000m, -100000, 5, -1, 0, -1, 6), + (-1.000000m, -1000000, 6, -1, 0, -1, 7), + (-1.0000000m, -10000000, 7, -1, 0, -1, 8), + (-1.00000000m, -100000000, 8, -1, 0, -1, 9), + (-1.000000000m, -1000000000, 9, -1, 0, -1, 10), + (-1.0000000000m, -10000000000, 10, -1, 0, -1, 11), + (-0.1m, -1, 1, -1, -1, -1, 2), + (-0.01m, -1, 2, -1, -2, -1, 3), + (-0.001m, -1, 3, -1, -3, -1, 4), + (-0.0001m, -1, 4, -1, -4, -1, 5), + (-0.00001m, -1, 5, -1, -5, -1, 6), + (-0.000001m, -1, 6, -1, -6, -1, 7), + (-0.0000001m, -1, 7, -1, -7, -1, 8), + (-0.00000001m, -1, 8, -1, -8, -1, 9), + (-0.000000001m, -1, 9, -1, -9, -1, 10), + (-0.0000000001m, -1, 10, -1, -10, -1, 11), + (-12345678.9m, -123456789, 1, -123456789, -1, -1, 9), + (-1234567.89m, -123456789, 2, -123456789, -2, -1, 9), + (-123456.789m, -123456789, 3, -123456789, -3, -1, 9), + (-12345.6789m, -123456789, 4, -123456789, -4, -1, 9), + (-1234.56789m, -123456789, 5, -123456789, -5, -1, 9), + (-123.456789m, -123456789, 6, -123456789, -6, -1, 9), + (-12.3456789m, -123456789, 7, -123456789, -7, -1, 9), + (-1.23456789m, -123456789, 8, -123456789, -8, -1, 9), + (-0.123456789m, -123456789, 9, -123456789, -9, -1, 10), + (-0.0123456789m, -123456789, 10, -123456789, -10, -1, 11), + (-0.00123456789m, -123456789, 11, -123456789, -11, -1, 12), + (-0.000123456789m, -123456789, 12, -123456789, -12, -1, 13), + (-0.0000123456789m, -123456789, 13, -123456789, -13, -1, 14), + (-0.00000123456789m, -123456789, 14, -123456789, -14, -1, 15), + (-0.000000123456789m, -123456789, 15, -123456789, -15, -1, 16), + (-0.0000000123456789m, -123456789, 16, -123456789, -16, -1, 17), + (-0.00000000123456789m, -123456789, 17, -123456789, -17, -1, 18), + (-0.000000000123456789m, -123456789, 18, -123456789, -18, -1, 19), + (-123.456m, -123456, 3, -123456, -3, -1, 6), + (-123.4560m, -1234560, 4, -123456, -3, -1, 7), + (-123.45600m, -12345600, 5, -123456, -3, -1, 8), + (-123.456000m, -123456000, 6, -123456, -3, -1, 9), + (-123.4560000m, -1234560000, 7, -123456, -3, -1, 10), + (-123.45600000m, -12345600000, 8, -123456, -3, -1, 11), + (-123.456000000m, -123456000000, 9, -123456, -3, -1, 12), + (-123.4560000000m, -1234560000000, 10, -123456, -3, -1, 13), + (-123.45600000000m, -12345600000000, 11, -123456, -3, -1, 14), + (-123.456000000000m, -123456000000000, 12, -123456, -3, -1, 15), + (-123.4560000000000m, -1234560000000000, 13, -123456, -3, -1, 16), + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach ((decimal value, BigInteger unscaledValue, int scale, BigInteger significand, int exponent, int sign, int precision) in Data) + yield return [value, unscaledValue, scale, significand, exponent, sign, precision]; + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoDoubleDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoDoubleDataAttribute.cs new file mode 100644 index 0000000..4790706 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoDoubleDataAttribute.cs @@ -0,0 +1,93 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class NumericsExtensionsToNumberInfoDoubleDataAttribute : DataAttribute +{ + private static readonly (double Value, BigInteger UnscaledValue, int Scale, BigInteger Significand, int Exponent, int Sign, int Precision)[] Data = + [ + (2.718281828459045, 2718281828459045L, 15, 2718281828459045L, -15, 1, 16), + (3.141592653589793, 3141592653589793, 15, 3141592653589793, -15, 1, 16), + (6.283185307179586, 6283185307179586, 15, 6283185307179586, -15, 1, 16), + (0.1, 1, 1, 1, -1, 1, 2), + (0.01, 1, 2, 1, -2, 1, 3), + (0.001, 1, 3, 1, -3, 1, 4), + (0.0001, 1, 4, 1, -4, 1, 5), + (1E-05, 1, 5, 1, -5, 1, 6), + (1E-06, 1, 6, 1, -6, 1, 7), + (1E-07, 1, 7, 1, -7, 1, 8), + (1E-08, 1, 8, 1, -8, 1, 9), + (1E-09, 1, 9, 1, -9, 1, 10), + (1E-10, 1, 10, 1, -10, 1, 11), + (12345678.9, 123456789, 1, 123456789, -1, 1, 9), + (1234567.89, 123456789, 2, 123456789, -2, 1, 9), + (123456.789, 123456789, 3, 123456789, -3, 1, 9), + (12345.6789, 123456789, 4, 123456789, -4, 1, 9), + (1234.56789, 123456789, 5, 123456789, -5, 1, 9), + (123.456789, 123456789, 6, 123456789, -6, 1, 9), + (12.3456789, 123456789, 7, 123456789, -7, 1, 9), + (1.23456789, 123456789, 8, 123456789, -8, 1, 9), + (0.123456789, 123456789, 9, 123456789, -9, 1, 10), + (0.0123456789, 123456789, 10, 123456789, -10, 1, 11), + (0.00123456789, 123456789, 11, 123456789, -11, 1, 12), + (0.000123456789, 123456789, 12, 123456789, -12, 1, 13), + (1.23456789E-05, 123456789, 13, 123456789, -13, 1, 14), + (1.23456789E-06, 123456789, 14, 123456789, -14, 1, 15), + (1.23456789E-07, 123456789, 15, 123456789, -15, 1, 16), + (1.23456789E-08, 123456789, 16, 123456789, -16, 1, 17), + (1.23456789E-09, 123456789, 17, 123456789, -17, 1, 18), + (1.23456789E-10, 123456789, 18, 123456789, -18, 1, 19), + (-0.1, -1, 1, -1, -1, -1, 2), + (-0.01, -1, 2, -1, -2, -1, 3), + (-0.001, -1, 3, -1, -3, -1, 4), + (-0.0001, -1, 4, -1, -4, -1, 5), + (-1E-05, -1, 5, -1, -5, -1, 6), + (-1E-06, -1, 6, -1, -6, -1, 7), + (-1E-07, -1, 7, -1, -7, -1, 8), + (-1E-08, -1, 8, -1, -8, -1, 9), + (-1E-09, -1, 9, -1, -9, -1, 10), + (-1E-10, -1, 10, -1, -10, -1, 11), + (-12345678.9, -123456789, 1, -123456789, -1, -1, 9), + (-1234567.89, -123456789, 2, -123456789, -2, -1, 9), + (-123456.789, -123456789, 3, -123456789, -3, -1, 9), + (-12345.6789, -123456789, 4, -123456789, -4, -1, 9), + (-1234.56789, -123456789, 5, -123456789, -5, -1, 9), + (-123.456789, -123456789, 6, -123456789, -6, -1, 9), + (-12.3456789, -123456789, 7, -123456789, -7, -1, 9), + (-1.23456789, -123456789, 8, -123456789, -8, -1, 9), + (-0.123456789, -123456789, 9, -123456789, -9, -1, 10), + (-0.0123456789, -123456789, 10, -123456789, -10, -1, 11), + (-0.00123456789, -123456789, 11, -123456789, -11, -1, 12), + (-0.000123456789, -123456789, 12, -123456789, -12, -1, 13), + (-1.23456789E-05, -123456789, 13, -123456789, -13, -1, 14), + (-1.23456789E-06, -123456789, 14, -123456789, -14, -1, 15), + (-1.23456789E-07, -123456789, 15, -123456789, -15, -1, 16), + (-1.23456789E-08, -123456789, 16, -123456789, -16, -1, 17), + (-1.23456789E-09, -123456789, 17, -123456789, -17, -1, 18), + (-1.23456789E-10, -123456789, 18, -123456789, -18, -1, 19) + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach ((double value, BigInteger unscaledValue, int scale, BigInteger significand, int exponent, int sign, int precision) in Data) + { + yield return [value, unscaledValue, scale, significand, exponent, sign, precision]; + } + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoFloatDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoFloatDataAttribute.cs new file mode 100644 index 0000000..e162940 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoFloatDataAttribute.cs @@ -0,0 +1,90 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class NumericsExtensionsToNumberInfoFloatDataAttribute : DataAttribute +{ + private static readonly (float Value, BigInteger UnscaledValue, int Scale, BigInteger Significand, int Exponent, int Sign, int Precision)[] Data = + [ + (0.1f, 1, 1, 1, -1, 1, 2), + (0.01f, 1, 2, 1, -2, 1, 3), + (0.001f, 1, 3, 1, -3, 1, 4), + (0.0001f, 1, 4, 1, -4, 1, 5), + (1E-05f, 1, 5, 1, -5, 1, 6), + (1E-06f, 1, 6, 1, -6, 1, 7), + (1E-07f, 1, 7, 1, -7, 1, 8), + (1E-08f, 1, 8, 1, -8, 1, 9), + (1E-09f, 1, 9, 1, -9, 1, 10), + (1E-10f, 1, 10, 1, -10, 1, 11), + (12345679f, 12345679, 0, 12345679, 0, 1, 8), + (1234567.9f, 12345679, 1, 12345679, -1, 1, 8), + (123456.79f, 12345679, 2, 12345679, -2, 1, 8), + (12345.679f, 12345679, 3, 12345679, -3, 1, 8), + (1234.5679f, 12345679, 4, 12345679, -4, 1, 8), + (123.45679f, 12345679, 5, 12345679, -5, 1, 8), + (12.345679f, 12345679, 6, 12345679, -6, 1, 8), + (1.2345679f, 12345679, 7, 12345679, -7, 1, 8), + (0.12345679f, 12345679, 8, 12345679, -8, 1, 9), + (0.012345679f, 12345679, 9, 12345679, -9, 1, 10), + (0.0012345678f, 12345678, 10, 12345678, -10, 1, 11), + (0.00012345679f, 12345679, 11, 12345679, -11, 1, 12), + (1.2345679E-05f, 12345679, 12, 12345679, -12, 1, 13), + (1.2345679E-06f, 12345679, 13, 12345679, -13, 1, 14), + (1.2345679E-07f, 12345679, 14, 12345679, -14, 1, 15), + (1.2345679E-08f, 12345679, 15, 12345679, -15, 1, 16), + (1.2345679E-09f, 12345679, 16, 12345679, -16, 1, 17), + (1.2345679E-10f, 12345679, 17, 12345679, -17, 1, 18), + (-0.1f, -1, 1, -1, -1, -1, 2), + (-0.01f, -1, 2, -1, -2, -1, 3), + (-0.001f, -1, 3, -1, -3, -1, 4), + (-0.0001f, -1, 4, -1, -4, -1, 5), + (-1E-05f, -1, 5, -1, -5, -1, 6), + (-1E-06f, -1, 6, -1, -6, -1, 7), + (-1E-07f, -1, 7, -1, -7, -1, 8), + (-1E-08f, -1, 8, -1, -8, -1, 9), + (-1E-09f, -1, 9, -1, -9, -1, 10), + (-1E-10f, -1, 10, -1, -10, -1, 11), + (-12345679f, -12345679, 0, -12345679, 0, -1, 8), + (-1234567.9f, -12345679, 1, -12345679, -1, -1, 8), + (-123456.79f, -12345679, 2, -12345679, -2, -1, 8), + (-12345.679f, -12345679, 3, -12345679, -3, -1, 8), + (-1234.5679f, -12345679, 4, -12345679, -4, -1, 8), + (-123.45679f, -12345679, 5, -12345679, -5, -1, 8), + (-12.345679f, -12345679, 6, -12345679, -6, -1, 8), + (-1.2345679f, -12345679, 7, -12345679, -7, -1, 8), + (-0.12345679f, -12345679, 8, -12345679, -8, -1, 9), + (-0.012345679f, -12345679, 9, -12345679, -9, -1, 10), + (-0.0012345678f, -12345678, 10, -12345678, -10, -1, 11), + (-0.00012345679f, -12345679, 11, -12345679, -11, -1, 12), + (-1.2345679E-05f, -12345679, 12, -12345679, -12, -1, 13), + (-1.2345679E-06f, -12345679, 13, -12345679, -13, -1, 14), + (-1.2345679E-07f, -12345679, 14, -12345679, -14, -1, 15), + (-1.2345679E-08f, -12345679, 15, -12345679, -15, -1, 16), + (-1.2345679E-09f, -12345679, 16, -12345679, -16, -1, 17), + (-1.2345679E-10f, -12345679, 17, -12345679, -17, -1, 18), + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach ((float value, BigInteger unscaledValue, int scale, BigInteger significand, int exponent, int sign, int precision) in Data) + { + yield return [value, unscaledValue, scale, significand, exponent, sign, precision]; + } + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoIntegerDataAttribute.cs b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoIntegerDataAttribute.cs new file mode 100644 index 0000000..c83d471 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/NumericsExtensionsToNumberInfoIntegerDataAttribute.cs @@ -0,0 +1,95 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using System.Reflection; +using Xunit.Sdk; + +namespace OnixLabs.Numerics.UnitTests.Data; + +public sealed class NumericsExtensionsToNumberInfoIntegerDataAttribute : DataAttribute +{ + private static readonly (Int128 Value, BigInteger UnscaledValue, int Scale, BigInteger Significand, int Exponent, int Sign, int Precision)[] Data = + [ + (0, 0, 0, 0, 0, 0, 1), + (1, 1, 0, 1, 0, 1, 1), + (2, 2, 0, 2, 0, 1, 1), + (3, 3, 0, 3, 0, 1, 1), + (4, 4, 0, 4, 0, 1, 1), + (5, 5, 0, 5, 0, 1, 1), + (6, 6, 0, 6, 0, 1, 1), + (7, 7, 0, 7, 0, 1, 1), + (8, 8, 0, 8, 0, 1, 1), + (9, 9, 0, 9, 0, 1, 1), + (10, 10, 0, 1, 1, 1, 2), + (100, 100, 0, 1, 2, 1, 3), + (1000, 1000, 0, 1, 3, 1, 4), + (10000, 10000, 0, 1, 4, 1, 5), + (100000, 100000, 0, 1, 5, 1, 6), + (1000000, 1000000, 0, 1, 6, 1, 7), + (10000000, 10000000, 0, 1, 7, 1, 8), + (100000000, 100000000, 0, 1, 8, 1, 9), + (1000000000, 1000000000, 0, 1, 9, 1, 10), + (10000000000, 10000000000, 0, 1, 10, 1, 11), + (-1, -1, 0, -1, 0, -1, 1), + (-2, -2, 0, -2, 0, -1, 1), + (-3, -3, 0, -3, 0, -1, 1), + (-4, -4, 0, -4, 0, -1, 1), + (-5, -5, 0, -5, 0, -1, 1), + (-6, -6, 0, -6, 0, -1, 1), + (-7, -7, 0, -7, 0, -1, 1), + (-8, -8, 0, -8, 0, -1, 1), + (-9, -9, 0, -9, 0, -1, 1), + (-10, -10, 0, -1, 1, -1, 2), + (-100, -100, 0, -1, 2, -1, 3), + (-1000, -1000, 0, -1, 3, -1, 4), + (-10000, -10000, 0, -1, 4, -1, 5), + (-100000, -100000, 0, -1, 5, -1, 6), + (-1000000, -1000000, 0, -1, 6, -1, 7), + (-10000000, -10000000, 0, -1, 7, -1, 8), + (-100000000, -100000000, 0, -1, 8, -1, 9), + (-1000000000, -1000000000, 0, -1, 9, -1, 10), + (-10000000000, -10000000000, 0, -1, 10, -1, 11), + (123456, 123456, 0, 123456, 0, 1, 6), + (123456000, 123456000, 0, 123456, 3, 1, 9), + (123456000000, 123456000000, 0, 123456, 6, 1, 12), + (-123456, -123456, 0, -123456, 0, -1, 6), + (-123456000, -123456000, 0, -123456, 3, -1, 9), + (-123456000000, -123456000000, 0, -123456, 6, -1, 12), + (sbyte.MaxValue, 127, 0, 127, 0, 1, 3), + (sbyte.MinValue, -128, 0, -128, 0, -1, 3), + (byte.MaxValue, 255, 0, 255, 0, 1, 3), + (byte.MinValue, 0, 0, 0, 0, 0, 1), + (short.MaxValue, 32767, 0, 32767, 0, 1, 5), + (short.MinValue, -32768, 0, -32768, 0, -1, 5), + (ushort.MaxValue, 65535, 0, 65535, 0, 1, 5), + (ushort.MinValue, 0, 0, 0, 0, 0, 1), + (int.MaxValue, 2147483647, 0, 2147483647, 0, 1, 10), + (int.MinValue, -2147483648, 0, -2147483648, 0, -1, 10), + (uint.MaxValue, 4294967295, 0, 4294967295, 0, 1, 10), + (uint.MinValue, 0, 0, 0, 0, 0, 1), + (long.MaxValue, 9223372036854775807, 0, 9223372036854775807, 0, 1, 19), + (long.MinValue, -9223372036854775808, 0, -9223372036854775808, 0, -1, 19), + (ulong.MaxValue, 18446744073709551615, 0, 18446744073709551615, 0, 1, 20), + (ulong.MinValue, 0, 0, 0, 0, 0, 1) + ]; + + public override IEnumerable GetData(MethodInfo testMethod) + { + foreach ((Int128 value, BigInteger unscaledValue, int scale, BigInteger significand, int exponent, int sign, int precision) in Data) + { + yield return [value, unscaledValue, scale, significand, exponent, sign, precision]; + } + } +} diff --git a/OnixLabs.Numerics.UnitTests.Data/OnixLabs.Numerics.UnitTests.Data.csproj b/OnixLabs.Numerics.UnitTests.Data/OnixLabs.Numerics.UnitTests.Data.csproj new file mode 100644 index 0000000..0d248c8 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/OnixLabs.Numerics.UnitTests.Data.csproj @@ -0,0 +1,28 @@ + + + net8.0 + enable + enable + + false + true + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/OnixLabs.Numerics.UnitTests.Data/TestDataGenerator.cs b/OnixLabs.Numerics.UnitTests.Data/TestDataGenerator.cs new file mode 100644 index 0000000..d15dcc2 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests.Data/TestDataGenerator.cs @@ -0,0 +1,952 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; + +namespace OnixLabs.Numerics.UnitTests.Data; + +internal static class TestDataGenerator +{ + private const int MinScale = 0; + private const int MaxScale = 28; + + private static readonly int[] DefaultValues = + [ + 0, + + +1, +2, +3, +4, +5, +6, +7, +8, +9, + +10, +100, +1_000, +10_000, +1_000_000_000, + +12, +123, +1_234, +12_345, +1_234_567_890, + + -1, -2, -3, -4, -5, -6, -7, -8, -9, + -10, -100, -1_000, -10_000, -1_000_000_000, + -12, -123, -1_234, -12_345, -1_234_567_890 + ]; + + private static readonly int[] DefaultScales = [0, 1, 2, 3, 4, 5, 10]; + + private static readonly ScaleMode[] DefaultModes = Enum.GetValues(); + + private static readonly decimal[] StaticValues = + [ + 0m, + 0.0m, + 0.00m, + 0.000m, + 0.0000m, + 0.00000m, + 0.000000m, + 0.0000000m, + 0.00000000m, + 0.000000000m, + 0.0000000000m, + 0.00000000000m, + 0.000000000000m, + 0.0000000000000m, + 0.00000000000000m, + 0.000000000000000m, + 0.0000000000000000m, + 0.00000000000000000m, + 0.000000000000000000m, + 0.0000000000000000000m, + 0.00000000000000000000m, + 0.000000000000000000000m, + 0.0000000000000000000000m, + 0.00000000000000000000000m, + 0.000000000000000000000000m, + 0.0000000000000000000000000m, + 0.00000000000000000000000000m, + 0.000000000000000000000000000m, + 1m, + 0.1m, + 0.01m, + 0.001m, + 0.0001m, + 0.00001m, + 0.000001m, + 0.0000001m, + 0.00000001m, + 0.000000001m, + 0.0000000001m, + 0.00000000001m, + 0.000000000001m, + 0.0000000000001m, + 0.00000000000001m, + 0.000000000000001m, + 0.0000000000000001m, + 0.00000000000000001m, + 0.000000000000000001m, + 0.0000000000000000001m, + 0.00000000000000000001m, + 0.000000000000000000001m, + 0.0000000000000000000001m, + 0.00000000000000000000001m, + 0.000000000000000000000001m, + 0.0000000000000000000000001m, + 0.00000000000000000000000001m, + 0.000000000000000000000000001m, + 1.0m, + 1.00m, + 1.000m, + 1.0000m, + 1.00000m, + 1.000000m, + 1.0000000m, + 1.00000000m, + 1.000000000m, + 1.0000000000m, + 1.00000000000m, + 1.000000000000m, + 1.0000000000000m, + 1.00000000000000m, + 1.000000000000000m, + 1.0000000000000000m, + 1.00000000000000000m, + 1.000000000000000000m, + 1.0000000000000000000m, + 1.00000000000000000000m, + 1.000000000000000000000m, + 1.0000000000000000000000m, + 1.00000000000000000000000m, + 1.000000000000000000000000m, + 1.0000000000000000000000000m, + 1.00000000000000000000000000m, + 1.000000000000000000000000000m, + 10m, + 0.10m, + 0.010m, + 0.0010m, + 0.00010m, + 0.000010m, + 0.0000010m, + 0.00000010m, + 0.000000010m, + 0.0000000010m, + 0.00000000010m, + 0.000000000010m, + 0.0000000000010m, + 0.00000000000010m, + 0.000000000000010m, + 0.0000000000000010m, + 0.00000000000000010m, + 0.000000000000000010m, + 0.0000000000000000010m, + 0.00000000000000000010m, + 0.000000000000000000010m, + 0.0000000000000000000010m, + 0.00000000000000000000010m, + 0.000000000000000000000010m, + 0.0000000000000000000000010m, + 0.00000000000000000000000010m, + 10.0m, + 10.00m, + 10.000m, + 10.0000m, + 10.00000m, + 10.000000m, + 10.0000000m, + 10.00000000m, + 10.000000000m, + 10.0000000000m, + 10.00000000000m, + 10.000000000000m, + 10.0000000000000m, + 10.00000000000000m, + 10.000000000000000m, + 10.0000000000000000m, + 10.00000000000000000m, + 10.000000000000000000m, + 10.0000000000000000000m, + 10.00000000000000000000m, + 10.000000000000000000000m, + 10.0000000000000000000000m, + 10.00000000000000000000000m, + 10.000000000000000000000000m, + 10.0000000000000000000000000m, + 10.00000000000000000000000000m, + 100m, + 0.100m, + 0.0100m, + 0.00100m, + 0.000100m, + 0.0000100m, + 0.00000100m, + 0.000000100m, + 0.0000000100m, + 0.00000000100m, + 0.000000000100m, + 0.0000000000100m, + 0.00000000000100m, + 0.000000000000100m, + 0.0000000000000100m, + 0.00000000000000100m, + 0.000000000000000100m, + 0.0000000000000000100m, + 0.00000000000000000100m, + 0.000000000000000000100m, + 0.0000000000000000000100m, + 0.00000000000000000000100m, + 0.000000000000000000000100m, + 0.0000000000000000000000100m, + 100.0m, + 100.00m, + 100.000m, + 100.0000m, + 100.00000m, + 100.000000m, + 100.0000000m, + 100.00000000m, + 100.000000000m, + 100.0000000000m, + 100.00000000000m, + 100.000000000000m, + 100.0000000000000m, + 100.00000000000000m, + 100.000000000000000m, + 100.0000000000000000m, + 100.00000000000000000m, + 100.000000000000000000m, + 100.0000000000000000000m, + 100.00000000000000000000m, + 100.000000000000000000000m, + 100.0000000000000000000000m, + 100.00000000000000000000000m, + 100.000000000000000000000000m, + 100.0000000000000000000000000m, + 127m, + 12.7m, + 1.27m, + 0.127m, + 0.0127m, + 0.00127m, + 0.000127m, + 0.0000127m, + 0.00000127m, + 0.000000127m, + 0.0000000127m, + 0.00000000127m, + 0.000000000127m, + 0.0000000000127m, + 0.00000000000127m, + 0.000000000000127m, + 0.0000000000000127m, + 0.00000000000000127m, + 0.000000000000000127m, + 0.0000000000000000127m, + 0.00000000000000000127m, + 0.000000000000000000127m, + 0.0000000000000000000127m, + 0.00000000000000000000127m, + 0.000000000000000000000127m, + 0.0000000000000000000000127m, + 127.0m, + 127.00m, + 127.000m, + 127.0000m, + 127.00000m, + 127.000000m, + 127.0000000m, + 127.00000000m, + 127.000000000m, + 127.0000000000m, + 127.00000000000m, + 127.000000000000m, + 127.0000000000000m, + 127.00000000000000m, + 127.000000000000000m, + 127.0000000000000000m, + 127.00000000000000000m, + 127.000000000000000000m, + 127.0000000000000000000m, + 127.00000000000000000000m, + 127.000000000000000000000m, + 127.0000000000000000000000m, + 127.00000000000000000000000m, + 127.000000000000000000000000m, + 127.0000000000000000000000000m, + 255m, + 25.5m, + 2.55m, + 0.255m, + 0.0255m, + 0.00255m, + 0.000255m, + 0.0000255m, + 0.00000255m, + 0.000000255m, + 0.0000000255m, + 0.00000000255m, + 0.000000000255m, + 0.0000000000255m, + 0.00000000000255m, + 0.000000000000255m, + 0.0000000000000255m, + 0.00000000000000255m, + 0.000000000000000255m, + 0.0000000000000000255m, + 0.00000000000000000255m, + 0.000000000000000000255m, + 0.0000000000000000000255m, + 0.00000000000000000000255m, + 0.000000000000000000000255m, + 0.0000000000000000000000255m, + 255.0m, + 255.00m, + 255.000m, + 255.0000m, + 255.00000m, + 255.000000m, + 255.0000000m, + 255.00000000m, + 255.000000000m, + 255.0000000000m, + 255.00000000000m, + 255.000000000000m, + 255.0000000000000m, + 255.00000000000000m, + 255.000000000000000m, + 255.0000000000000000m, + 255.00000000000000000m, + 255.000000000000000000m, + 255.0000000000000000000m, + 255.00000000000000000000m, + 255.000000000000000000000m, + 255.0000000000000000000000m, + 255.00000000000000000000000m, + 255.000000000000000000000000m, + 255.0000000000000000000000000m, + 32767m, + 3276.7m, + 327.67m, + 32.767m, + 3.2767m, + 0.32767m, + 0.032767m, + 0.0032767m, + 0.00032767m, + 0.000032767m, + 0.0000032767m, + 0.00000032767m, + 0.000000032767m, + 0.0000000032767m, + 0.00000000032767m, + 0.000000000032767m, + 0.0000000000032767m, + 0.00000000000032767m, + 0.000000000000032767m, + 0.0000000000000032767m, + 0.00000000000000032767m, + 0.000000000000000032767m, + 0.0000000000000000032767m, + 0.00000000000000000032767m, + 32767.0m, + 32767.00m, + 32767.000m, + 32767.0000m, + 32767.00000m, + 32767.000000m, + 32767.0000000m, + 32767.00000000m, + 32767.000000000m, + 32767.0000000000m, + 32767.00000000000m, + 32767.000000000000m, + 32767.0000000000000m, + 32767.00000000000000m, + 32767.000000000000000m, + 32767.0000000000000000m, + 32767.00000000000000000m, + 32767.000000000000000000m, + 32767.0000000000000000000m, + 32767.00000000000000000000m, + 32767.000000000000000000000m, + 32767.0000000000000000000000m, + 32767.00000000000000000000000m, + 65535m, + 6553.5m, + 655.35m, + 65.535m, + 6.5535m, + 0.65535m, + 0.065535m, + 0.0065535m, + 0.00065535m, + 0.000065535m, + 0.0000065535m, + 0.00000065535m, + 0.000000065535m, + 0.0000000065535m, + 0.00000000065535m, + 0.000000000065535m, + 0.0000000000065535m, + 0.00000000000065535m, + 0.000000000000065535m, + 0.0000000000000065535m, + 0.00000000000000065535m, + 0.000000000000000065535m, + 0.0000000000000000065535m, + 0.00000000000000000065535m, + 65535.0m, + 65535.00m, + 65535.000m, + 65535.0000m, + 65535.00000m, + 65535.000000m, + 65535.0000000m, + 65535.00000000m, + 65535.000000000m, + 65535.0000000000m, + 65535.00000000000m, + 65535.000000000000m, + 65535.0000000000000m, + 65535.00000000000000m, + 65535.000000000000000m, + 65535.0000000000000000m, + 65535.00000000000000000m, + 65535.000000000000000000m, + 65535.0000000000000000000m, + 65535.00000000000000000000m, + 65535.000000000000000000000m, + 65535.0000000000000000000000m, + 65535.00000000000000000000000m, + 2147483647m, + 214748364.7m, + 21474836.47m, + 2147483.647m, + 214748.3647m, + 21474.83647m, + 2147.483647m, + 214.7483647m, + 21.47483647m, + 2.147483647m, + 0.2147483647m, + 0.02147483647m, + 0.002147483647m, + 0.0002147483647m, + 0.00002147483647m, + 0.000002147483647m, + 0.0000002147483647m, + 0.00000002147483647m, + 0.000000002147483647m, + 2147483647.0m, + 2147483647.00m, + 2147483647.000m, + 2147483647.0000m, + 2147483647.00000m, + 2147483647.000000m, + 2147483647.0000000m, + 2147483647.00000000m, + 2147483647.000000000m, + 2147483647.0000000000m, + 2147483647.00000000000m, + 2147483647.000000000000m, + 2147483647.0000000000000m, + 2147483647.00000000000000m, + 2147483647.000000000000000m, + 2147483647.0000000000000000m, + 2147483647.00000000000000000m, + 2147483647.000000000000000000m, + 4294967295m, + 429496729.5m, + 42949672.95m, + 4294967.295m, + 429496.7295m, + 42949.67295m, + 4294.967295m, + 429.4967295m, + 42.94967295m, + 4.294967295m, + 0.4294967295m, + 0.04294967295m, + 0.004294967295m, + 0.0004294967295m, + 0.00004294967295m, + 0.000004294967295m, + 0.0000004294967295m, + 0.00000004294967295m, + 0.000000004294967295m, + 4294967295.0m, + 4294967295.00m, + 4294967295.000m, + 4294967295.0000m, + 4294967295.00000m, + 4294967295.000000m, + 4294967295.0000000m, + 4294967295.00000000m, + 4294967295.000000000m, + 4294967295.0000000000m, + 4294967295.00000000000m, + 4294967295.000000000000m, + 4294967295.0000000000000m, + 4294967295.00000000000000m, + 4294967295.000000000000000m, + 4294967295.0000000000000000m, + 4294967295.00000000000000000m, + 4294967295.000000000000000000m, + 9223372036854775807m, + 922337203685477580.7m, + 92233720368547758.07m, + 9223372036854775.807m, + 922337203685477.5807m, + 92233720368547.75807m, + 9223372036854.775807m, + 922337203685.4775807m, + 92233720368.54775807m, + 9223372036.854775807m, + 9223372036854775807.0m, + 9223372036854775807.00m, + 9223372036854775807.000m, + 9223372036854775807.0000m, + 9223372036854775807.00000m, + 9223372036854775807.000000m, + 9223372036854775807.0000000m, + 9223372036854775807.00000000m, + 9223372036854775807.000000000m, + 18446744073709551615m, + 1844674407370955161.5m, + 184467440737095516.15m, + 18446744073709551.615m, + 1844674407370955.1615m, + 184467440737095.51615m, + 18446744073709.551615m, + 1844674407370.9551615m, + 184467440737.09551615m, + 18446744073709551615.0m, + 18446744073709551615.00m, + 18446744073709551615.000m, + 18446744073709551615.0000m, + 18446744073709551615.00000m, + 18446744073709551615.000000m, + 18446744073709551615.0000000m, + 18446744073709551615.00000000m, + -1m, + -0.1m, + -0.01m, + -0.001m, + -0.0001m, + -0.00001m, + -0.000001m, + -0.0000001m, + -0.00000001m, + -0.000000001m, + -0.0000000001m, + -0.00000000001m, + -0.000000000001m, + -0.0000000000001m, + -0.00000000000001m, + -0.000000000000001m, + -0.0000000000000001m, + -0.00000000000000001m, + -0.000000000000000001m, + -0.0000000000000000001m, + -0.00000000000000000001m, + -0.000000000000000000001m, + -0.0000000000000000000001m, + -0.00000000000000000000001m, + -0.000000000000000000000001m, + -0.0000000000000000000000001m, + -0.00000000000000000000000001m, + -0.000000000000000000000000001m, + -1.0m, + -1.00m, + -1.000m, + -1.0000m, + -1.00000m, + -1.000000m, + -1.0000000m, + -1.00000000m, + -1.000000000m, + -1.0000000000m, + -1.00000000000m, + -1.000000000000m, + -1.0000000000000m, + -1.00000000000000m, + -1.000000000000000m, + -1.0000000000000000m, + -1.00000000000000000m, + -1.000000000000000000m, + -1.0000000000000000000m, + -1.00000000000000000000m, + -1.000000000000000000000m, + -1.0000000000000000000000m, + -1.00000000000000000000000m, + -1.000000000000000000000000m, + -1.0000000000000000000000000m, + -1.00000000000000000000000000m, + -1.000000000000000000000000000m, + -10m, + -0.10m, + -0.010m, + -0.0010m, + -0.00010m, + -0.000010m, + -0.0000010m, + -0.00000010m, + -0.000000010m, + -0.0000000010m, + -0.00000000010m, + -0.000000000010m, + -0.0000000000010m, + -0.00000000000010m, + -0.000000000000010m, + -0.0000000000000010m, + -0.00000000000000010m, + -0.000000000000000010m, + -0.0000000000000000010m, + -0.00000000000000000010m, + -0.000000000000000000010m, + -0.0000000000000000000010m, + -0.00000000000000000000010m, + -0.000000000000000000000010m, + -0.0000000000000000000000010m, + -0.00000000000000000000000010m, + -10.0m, + -10.00m, + -10.000m, + -10.0000m, + -10.00000m, + -10.000000m, + -10.0000000m, + -10.00000000m, + -10.000000000m, + -10.0000000000m, + -10.00000000000m, + -10.000000000000m, + -10.0000000000000m, + -10.00000000000000m, + -10.000000000000000m, + -10.0000000000000000m, + -10.00000000000000000m, + -10.000000000000000000m, + -10.0000000000000000000m, + -10.00000000000000000000m, + -10.000000000000000000000m, + -10.0000000000000000000000m, + -10.00000000000000000000000m, + -10.000000000000000000000000m, + -10.0000000000000000000000000m, + -10.00000000000000000000000000m, + -100m, + -0.100m, + -0.0100m, + -0.00100m, + -0.000100m, + -0.0000100m, + -0.00000100m, + -0.000000100m, + -0.0000000100m, + -0.00000000100m, + -0.000000000100m, + -0.0000000000100m, + -0.00000000000100m, + -0.000000000000100m, + -0.0000000000000100m, + -0.00000000000000100m, + -0.000000000000000100m, + -0.0000000000000000100m, + -0.00000000000000000100m, + -0.000000000000000000100m, + -0.0000000000000000000100m, + -0.00000000000000000000100m, + -0.000000000000000000000100m, + -0.0000000000000000000000100m, + -100.0m, + -100.00m, + -100.000m, + -100.0000m, + -100.00000m, + -100.000000m, + -100.0000000m, + -100.00000000m, + -100.000000000m, + -100.0000000000m, + -100.00000000000m, + -100.000000000000m, + -100.0000000000000m, + -100.00000000000000m, + -100.000000000000000m, + -100.0000000000000000m, + -100.00000000000000000m, + -100.000000000000000000m, + -100.0000000000000000000m, + -100.00000000000000000000m, + -100.000000000000000000000m, + -100.0000000000000000000000m, + -100.00000000000000000000000m, + -100.000000000000000000000000m, + -100.0000000000000000000000000m, + -128m, + -12.8m, + -1.28m, + -0.128m, + -0.0128m, + -0.00128m, + -0.000128m, + -0.0000128m, + -0.00000128m, + -0.000000128m, + -0.0000000128m, + -0.00000000128m, + -0.000000000128m, + -0.0000000000128m, + -0.00000000000128m, + -0.000000000000128m, + -0.0000000000000128m, + -0.00000000000000128m, + -0.000000000000000128m, + -0.0000000000000000128m, + -0.00000000000000000128m, + -0.000000000000000000128m, + -0.0000000000000000000128m, + -0.00000000000000000000128m, + -0.000000000000000000000128m, + -0.0000000000000000000000128m, + -128.0m, + -128.00m, + -128.000m, + -128.0000m, + -128.00000m, + -128.000000m, + -128.0000000m, + -128.00000000m, + -128.000000000m, + -128.0000000000m, + -128.00000000000m, + -128.000000000000m, + -128.0000000000000m, + -128.00000000000000m, + -128.000000000000000m, + -128.0000000000000000m, + -128.00000000000000000m, + -128.000000000000000000m, + -128.0000000000000000000m, + -128.00000000000000000000m, + -128.000000000000000000000m, + -128.0000000000000000000000m, + -128.00000000000000000000000m, + -128.000000000000000000000000m, + -128.0000000000000000000000000m, + -32768m, + -3276.8m, + -327.68m, + -32.768m, + -3.2768m, + -0.32768m, + -0.032768m, + -0.0032768m, + -0.00032768m, + -0.000032768m, + -0.0000032768m, + -0.00000032768m, + -0.000000032768m, + -0.0000000032768m, + -0.00000000032768m, + -0.000000000032768m, + -0.0000000000032768m, + -0.00000000000032768m, + -0.000000000000032768m, + -0.0000000000000032768m, + -0.00000000000000032768m, + -0.000000000000000032768m, + -0.0000000000000000032768m, + -0.00000000000000000032768m, + -32768.0m, + -32768.00m, + -32768.000m, + -32768.0000m, + -32768.00000m, + -32768.000000m, + -32768.0000000m, + -32768.00000000m, + -32768.000000000m, + -32768.0000000000m, + -32768.00000000000m, + -32768.000000000000m, + -32768.0000000000000m, + -32768.00000000000000m, + -32768.000000000000000m, + -32768.0000000000000000m, + -32768.00000000000000000m, + -32768.000000000000000000m, + -32768.0000000000000000000m, + -32768.00000000000000000000m, + -32768.000000000000000000000m, + -32768.0000000000000000000000m, + -32768.00000000000000000000000m, + -2147483648m, + -214748364.8m, + -21474836.48m, + -2147483.648m, + -214748.3648m, + -21474.83648m, + -2147.483648m, + -214.7483648m, + -21.47483648m, + -2.147483648m, + -0.2147483648m, + -0.02147483648m, + -0.002147483648m, + -0.0002147483648m, + -0.00002147483648m, + -0.000002147483648m, + -0.0000002147483648m, + -0.00000002147483648m, + -0.000000002147483648m, + -2147483648.0m, + -2147483648.00m, + -2147483648.000m, + -2147483648.0000m, + -2147483648.00000m, + -2147483648.000000m, + -2147483648.0000000m, + -2147483648.00000000m, + -2147483648.000000000m, + -2147483648.0000000000m, + -2147483648.00000000000m, + -2147483648.000000000000m, + -2147483648.0000000000000m, + -2147483648.00000000000000m, + -2147483648.000000000000000m, + -2147483648.0000000000000000m, + -2147483648.00000000000000000m, + -2147483648.000000000000000000m, + -9223372036854775808m, + -922337203685477580.8m, + -92233720368547758.08m, + -9223372036854775.808m, + -922337203685477.5808m, + -92233720368547.75808m, + -9223372036854.775808m, + -922337203685.4775808m, + -92233720368.54775808m, + -9223372036.854775808m, + -9223372036854775808.0m, + -9223372036854775808.00m, + -9223372036854775808.000m, + -9223372036854775808.0000m, + -9223372036854775808.00000m, + -9223372036854775808.000000m, + -9223372036854775808.0000000m, + -9223372036854775808.00000000m, + -9223372036854775808.000000000m, + ]; + + private static readonly CultureInfo[] Cultures = + [ + CultureInfo.InvariantCulture, + CultureInfo.GetCultureInfo("ar-001"), // Arabic (world) + CultureInfo.GetCultureInfo("ar-AE"), // Arabic (United Arab Emirates) + CultureInfo.GetCultureInfo("en-001"), // English (world) + CultureInfo.GetCultureInfo("en-150"), // English (Europe) + CultureInfo.GetCultureInfo("en-GB"), // English (United Kingdom) + CultureInfo.GetCultureInfo("en-US"), // English (United States) + CultureInfo.GetCultureInfo("en-US-POSIX"), // English (United States, Computer) + CultureInfo.GetCultureInfo("en-CV"), // English (Cape Verde) + CultureInfo.GetCultureInfo("kea-CV"), // Kabuverdianu (Cape Verde) + CultureInfo.GetCultureInfo("pt-CV"), // Portuguese (Cape Verde) + CultureInfo.GetCultureInfo("eu"), // Basque + CultureInfo.GetCultureInfo("eu-ES"), // Basque (Spain) + CultureInfo.GetCultureInfo("bg-BG"), // Bulgarian (Bulgaria) + CultureInfo.GetCultureInfo("de-DE"), // German (Germany) + CultureInfo.GetCultureInfo("es-ES"), // Spanish (Spain) + CultureInfo.GetCultureInfo("fi-FI"), // Finnish (Finland) + CultureInfo.GetCultureInfo("fo-FO"), // Faroese (Faroe Islands) + CultureInfo.GetCultureInfo("fr-FR"), // French (France) + CultureInfo.GetCultureInfo("hr-HR"), // Croatian (Croatia) + CultureInfo.GetCultureInfo("hu-HU"), // Hungarian (Hungary) + CultureInfo.GetCultureInfo("id-ID"), // Indonesian (Indonesia) + CultureInfo.GetCultureInfo("is-IS"), // Icelandic (Iceland) + CultureInfo.GetCultureInfo("it-IT"), // Italian (Italy) + CultureInfo.GetCultureInfo("lt-LT"), // Lithuanian (Lithuania) + CultureInfo.GetCultureInfo("lv-LV"), // Latvian (Latvia) + CultureInfo.GetCultureInfo("mg-MG"), // Malagasy (Madagascar) + CultureInfo.GetCultureInfo("mk-MK"), // Macedonian (North Macedonia) + CultureInfo.GetCultureInfo("mn-MN"), // Mongolian (Mongolia) + CultureInfo.GetCultureInfo("mt-MT"), // Maltese (Malta) + CultureInfo.GetCultureInfo("nl-NL"), // Dutch (Netherlands) + CultureInfo.GetCultureInfo("pl-PL"), // Polish (Poland) + CultureInfo.GetCultureInfo("pt-PT"), // Portuguese (Portugal) + CultureInfo.GetCultureInfo("ro-RO"), // Romanian (Romania) + CultureInfo.GetCultureInfo("ru-RU"), // Russian (Russia) + CultureInfo.GetCultureInfo("rw-RW"), // Kinyarwanda (Rwanda) + CultureInfo.GetCultureInfo("se-SE"), // Northern Sami (Sweden) + CultureInfo.GetCultureInfo("sk-SK"), // Slovak (Slovakia) + CultureInfo.GetCultureInfo("so-SO"), // Somali (Somalia) + CultureInfo.GetCultureInfo("th-TH"), // Thai (Thailand) + CultureInfo.GetCultureInfo("to-TO"), // Tongan (Tonga) + CultureInfo.GetCultureInfo("tr-TR"), // Turkish (Türkiye) + CultureInfo.GetCultureInfo("zh-Hans-CN"), // Chinese, Simplified (China mainland) + CultureInfo.GetCultureInfo("zh-Hant-CN"), // Chinese, Traditional (China mainland) + ]; + + public static IEnumerable GenerateScaledValues( + IEnumerable? values = null, + IEnumerable? scales = null, + IEnumerable? modes = null) + { + foreach (ScaleMode mode in modes ?? DefaultModes) + foreach (int value in values ?? DefaultValues) + foreach (int scale in scales ?? DefaultScales) + yield return value.ToDecimal(scale, mode); + } + + public static IEnumerable GenerateRandomValues(int count = 100, int seed = default) + { + Random random = new(seed); + + for (int index = 0; index < count; index++) + yield return random.Next(int.MinValue, int.MaxValue).ToDecimal(random.Next(0, 10)); + } + + public static IEnumerable GenerateScaledMaxValues() + { + foreach (int scale in GenerateScaleValues()) + yield return ((Int128)decimal.MaxValue).ToDecimal(scale); + } + + public static IEnumerable GenerateScaledMinValues() + { + foreach (int scale in GenerateScaleValues()) + yield return ((Int128)decimal.MinValue).ToDecimal(scale); + } + + public static IEnumerable GenerateConstantValues() + { + yield return decimal.Zero; + yield return decimal.One; + yield return decimal.MinusOne; + yield return decimal.MaxValue; + yield return decimal.MinValue; + } + + public static IEnumerable GenerateStaticValues() + { + return StaticValues; + } + + public static IEnumerable GenerateCultures() + { + return Cultures; + } + + public static IEnumerable GetMidpointRoundingModes() + { + return Enum.GetValues(); + } + + public static IEnumerable GenerateScaleValues(int min = MinScale, int max = MaxScale) + { + for (int scale = min; scale <= max; scale++) yield return scale; + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticAbsTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticAbsTests.cs new file mode 100644 index 0000000..dabd13e --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticAbsTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticAbsTests +{ + [BigDecimalArithmeticAbsData] + [Theory(DisplayName = "BigDecimal.Abs should produce the expected result.")] + public void BigDecimalAbsShouldProduceExpectedResult(decimal value) + { + // Given + decimal expected = decimal.Abs(value); + + // When + BigDecimal actual = BigDecimal.Abs(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticAdditionTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticAdditionTests.cs new file mode 100644 index 0000000..80fd93e --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticAdditionTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticAdditionTests +{ + [BigDecimalArithmeticAdditionData] + [Theory(DisplayName = "BigDecimal.Add should produce the expected result.")] + public void BigDecimalAddShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + decimal expected = left + right; + + // When + BigDecimal actual = BigDecimal.Add(left, right).Round(expected.Scale); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticDecrementTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticDecrementTests.cs new file mode 100644 index 0000000..139206e --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticDecrementTests.cs @@ -0,0 +1,52 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticDecrementTests +{ + [Theory(DisplayName = "BigDecimal.Decrement should produce the expected result.")] + [InlineData(-1, -2)] + [InlineData(0, -1)] + [InlineData(1, 0)] + [InlineData(10, 9)] + [InlineData(1000, 999)] + [InlineData(-1000, -1001)] + public void BigDecimalDecrementShouldProduceExpectedResult(double value, double expected) + { + // When + BigDecimal actual = BigDecimal.Decrement(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BigDecimal.DecrementFraction should produce the expected result.")] + [InlineData(-1.2, -1.3)] + [InlineData(0.2, 0.1)] + [InlineData(1.02, 1.01)] + [InlineData(10.999, 10.998)] + [InlineData(1000.02, 1000.01)] + [InlineData(-1000.01, -1000.02)] + public void BigDecimalDecrementFractionShouldProduceExpectedResult(double value, double expected) + { + // When + BigDecimal actual = BigDecimal.DecrementFraction(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticDivisionTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticDivisionTests.cs new file mode 100644 index 0000000..e08a57b --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticDivisionTests.cs @@ -0,0 +1,60 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticDivisionTests +{ + private static readonly BigDecimalDivisionEqualityComparer Comparer = new(); + + [BigDecimalArithmeticDivisionData] + [Theory(DisplayName = "BigDecimal.Divide should produce the expected result")] + public void BigDecimalDivideShouldProduceExpectedResult(decimal left, decimal right, MidpointRounding mode, Guid _) + { + // Given + decimal expected = decimal.Round(left / right, left.Scale, mode); + + // When + BigDecimal actual = BigDecimal.Divide(left, right, mode); + + // Then + Assert.Equal(expected, actual, Comparer); + } + + /// + /// Represents an equality comparer that is used specifically for tests. Notably, this comparer allows + /// for deltas of zero, or one for cases where provides greater rounding accuracy than . + /// + private sealed class BigDecimalDivisionEqualityComparer : IEqualityComparer + { + public bool Equals(BigDecimal x, BigDecimal y) + { + int scale = BigDecimal.MaxScale(x, y); + (BigInteger left, BigInteger right) = BigDecimal.NormalizeUnscaledValues(x, y); + BigDecimal leftNormalized = new(left, scale); + BigDecimal rightNormalized = new(right, scale); + + return (int)GenericMath.Delta(leftNormalized.UnscaledValue, rightNormalized.UnscaledValue) is 1 or 0; + } + + public int GetHashCode(BigDecimal obj) + { + return HashCode.Combine(obj.ToNumberInfo()); + } + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticIncrementTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticIncrementTests.cs new file mode 100644 index 0000000..912ce6c --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticIncrementTests.cs @@ -0,0 +1,52 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticIncrementTests +{ + [Theory(DisplayName = "BigDecimal.Increment should produce the expected result.")] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 2)] + [InlineData(10, 11)] + [InlineData(1000, 1001)] + [InlineData(-1000, -999)] + public void BigDecimalIncrementShouldProduceExpectedResult(double value, double expected) + { + // When + BigDecimal actual = BigDecimal.Increment(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BigDecimal.IncrementFraction should produce the expected result.")] + [InlineData(-1.2, -1.1)] + [InlineData(0.2, 0.3)] + [InlineData(1.02, 1.03)] + [InlineData(10.998, 10.999)] + [InlineData(1000.02, 1000.03)] + [InlineData(-1000.02, -1000.01)] + public void BigDecimalIncrementFractionShouldProduceExpectedResult(double value, double expected) + { + // When + BigDecimal actual = BigDecimal.IncrementFraction(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticModulusTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticModulusTests.cs new file mode 100644 index 0000000..b31f53d --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticModulusTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticModulusTests +{ + [BigDecimalArithmeticModulusData] + [Theory(DisplayName = "BigDecimal.Mod should produce the expected result")] + public void BigDecimalModShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + decimal expected = left % right; + + // When + BigDecimal actual = BigDecimal.Mod(left, right).Round(expected.Scale); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticMultiplicationTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticMultiplicationTests.cs new file mode 100644 index 0000000..fc3f252 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticMultiplicationTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticMultiplicationTests +{ + [BigDecimalArithmeticMultiplicationData] + [Theory(DisplayName = "BigDecimal.Multiply should produce the expected result")] + public void BigDecimalMultiplyShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + decimal expected = left * right; + + // When + BigDecimal actual = BigDecimal.Multiply(left, right).Round(expected.Scale); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticPowTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticPowTests.cs new file mode 100644 index 0000000..724b1e2 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticPowTests.cs @@ -0,0 +1,32 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticPowTests +{ + [BigDecimalArithmeticPowData] + [Theory(DisplayName = "BigDecimal.Pow should produce the expected result")] + public void BigDecimalPowShouldProduceExpectedResult(decimal value, int exponent, decimal expected) + { + // When + BigDecimal actual = BigDecimal.Pow(value, exponent); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticRoundTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticRoundTests.cs new file mode 100644 index 0000000..27bf4d1 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticRoundTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticRoundTests +{ + [BigDecimalArithmeticRoundData] + [Theory(DisplayName = "BigDecimal.Round should produce the correct result")] + public void BigDecimalRoundShouldProduceExpectedResult(decimal value, byte scale, MidpointRounding mode, Guid _) + { + // Given + decimal expected = decimal.Round(value, scale, mode); + + // When + BigDecimal actual = BigDecimal.Round(value, scale, mode); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticScaleTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticScaleTests.cs new file mode 100644 index 0000000..aebc0f6 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticScaleTests.cs @@ -0,0 +1,32 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticScaleTests +{ + [BigDecimalArithmeticScaleData] + [Theory(DisplayName = "BigDecimal.SetScale should produce the expected result")] + public void BigDecimalSetScaleShouldProduceExpectedResult(decimal value, int scale, MidpointRounding mode, decimal expected) + { + // When + BigDecimal actual = value.ToBigDecimal().SetScale(scale, mode); + + // then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticSubtractionTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticSubtractionTests.cs new file mode 100644 index 0000000..8d11e93 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticSubtractionTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticSubtractionTests +{ + [BigDecimalArithmeticSubtractionData] + [Theory(DisplayName = "BigDecimal.Subtract should produce the expected result.")] + public void BigDecimalSubtractShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + decimal expected = left - right; + + // When + BigDecimal actual = BigDecimal.Subtract(left, right).Round(expected.Scale); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticTrimTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticTrimTests.cs new file mode 100644 index 0000000..30007ca --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticTrimTests.cs @@ -0,0 +1,32 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticTrimTests +{ + [BigDecimalArithmeticTrimData] + [Theory(DisplayName = "BigDecimal.TrimTrailingZeros should produce the expected result")] + public void BigDecimalTrimTrailingZerosShouldProduceExpectedResult(decimal value, decimal expected) + { + // When + BigDecimal actual = BigDecimal.TrimTrailingZeros(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticTruncateTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticTruncateTests.cs new file mode 100644 index 0000000..1caffc6 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticTruncateTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticTruncateTests +{ + [BigDecimalArithmeticTruncateData] + [Theory(DisplayName = "BigDecimal.Truncate should produce the expected result")] + public void BigDecimalTruncateShouldProduceExpectedResult(decimal value, Guid _) + { + // Given + decimal expected = decimal.Truncate(value); + + // When + BigDecimal actual = BigDecimal.Truncate(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticUnaryAdditionTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticUnaryAdditionTests.cs new file mode 100644 index 0000000..df48ba0 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticUnaryAdditionTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticUnaryAdditionTests +{ + [BigDecimalArithmeticUnaryAdditionData] + [Theory(DisplayName = "BigDecimal.UnaryAdd should produce the expected result")] + public void BigDecimalUnaryAddShouldProduceExpectedResult(decimal value) + { + // Given + decimal expected = +value; + + // When + BigDecimal actual = BigDecimal.UnaryAdd(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticUnarySubtractionTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticUnarySubtractionTests.cs new file mode 100644 index 0000000..a2e81bf --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalArithmeticUnarySubtractionTests.cs @@ -0,0 +1,35 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalArithmeticUnarySubtractionTests +{ + [BigDecimalArithmeticUnarySubtractionData] + [Theory(DisplayName = "BigDecimal.UnarySubtract should produce the expected result")] + public void BigDecimalUnarySubtractShouldProduceExpectedResult(decimal value) + { + // Given + decimal expected = -value; + + // When + BigDecimal actual = BigDecimal.UnarySubtract(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalComparableMinMaxTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalComparableMinMaxTests.cs new file mode 100644 index 0000000..fa81607 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalComparableMinMaxTests.cs @@ -0,0 +1,167 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalComparableMinMaxTests +{ + [Theory(DisplayName = "BigDecimal.Min should produce the expected result")] + [InlineData(0, 0, 0)] + [InlineData(1, 0, 0)] + [InlineData(1, 1, 1)] + [InlineData(1.1, 0.1, 0.1)] + [InlineData(1.1, 1.2, 1.1)] + [InlineData(0.01, 0.02, 0.01)] + [InlineData(-0.01, -0.02, -0.02)] + public void BigDecimalMinShouldProduceExpectedResult(double left, double right, double expected) + { + // When + BigDecimal actual = BigDecimal.Min(left, right); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BigDecimal.Max should produce the expected result")] + [InlineData(0, 0, 0)] + [InlineData(1, 0, 1)] + [InlineData(1, 1, 1)] + [InlineData(1.1, 0.1, 1.1)] + [InlineData(1.1, 1.2, 1.2)] + [InlineData(0.01, 0.02, 0.02)] + [InlineData(-0.01, -0.02, -0.01)] + public void BigDecimalMaxShouldProduceExpectedResult(double left, double right, double expected) + { + // When + BigDecimal actual = BigDecimal.Max(left, right); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BigDecimal.MinMax should produce the expected result")] + [InlineData(0, 0, 0, 0)] + [InlineData(1, 0, 0, 1)] + [InlineData(1, 1, 1, 1)] + [InlineData(1.1, 0.1, 0.1, 1.1)] + [InlineData(1.1, 1.2, 1.1, 1.2)] + [InlineData(0.01, 0.02, 0.01, 0.02)] + [InlineData(-0.01, -0.02, -0.02, -0.01)] + public void BigDecimalMinMaxShouldProduceExpectedResult(double left, double right, double expectedMin, double expectedMax) + { + // When + (BigDecimal actualMin, BigDecimal actualMax) = BigDecimal.MinMax(left, right); + + // Then + Assert.Equal(expectedMin, actualMin); + Assert.Equal(expectedMax, actualMax); + } + + [Theory(DisplayName = "BigDecimal.MinMagnitude should produce expected result")] + [InlineData(0, 0, 0)] + [InlineData(0, 1, 0)] + [InlineData(0, -1, 0)] + [InlineData(-1, 1, -1)] + [InlineData(1, 2, 1)] + [InlineData(1, -2, 1)] + [InlineData(-1, 2, -1)] + [InlineData(-1, -2, -1)] + [InlineData(123.456, 456.789, 123.456)] + [InlineData(-123.456, 456.789, -123.456)] + [InlineData(123.456, -456.789, 123.456)] + [InlineData(-123.456, -456.789, -123.456)] + [InlineData(0.1, 0.01, 0.01)] + [InlineData(0.1, -0.01, -0.01)] + [InlineData(-0.1, 0.01, 0.01)] + [InlineData(-0.1, -0.01, -0.01)] + public void BigDecimalMinMagnitudeShouldProduceExpectedResult(double left, double right, double expected) + { + // When + BigDecimal actual = BigDecimal.MinMagnitude(left, right); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BigDecimal.MaxMagnitude should produce expected result")] + [InlineData(0, 0, 0)] + [InlineData(0, 1, 1)] + [InlineData(0, -1, -1)] + [InlineData(-1, 1, 1)] + [InlineData(1, 2, 2)] + [InlineData(1, -2, -2)] + [InlineData(-1, 2, 2)] + [InlineData(-1, -2, -2)] + [InlineData(123.456, 456.789, 456.789)] + [InlineData(-123.456, 456.789, 456.789)] + [InlineData(123.456, -456.789, -456.789)] + [InlineData(-123.456, -456.789, -456.789)] + [InlineData(0.1, 0.01, 0.1)] + [InlineData(0.1, -0.01, 0.1)] + [InlineData(-0.1, 0.01, -0.1)] + [InlineData(-0.1, -0.01, -0.1)] + public void BigDecimalMaxMagnitudeShouldProduceExpectedResult(double left, double right, double expected) + { + // When + BigDecimal actual = BigDecimal.MaxMagnitude(left, right); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BigDecimal.MinScale should produce the expected result")] + [InlineData(0, 0, 0)] + [InlineData(0.1, 0, 0)] + [InlineData(0.01, 0.1, 1)] + [InlineData(0.001, 0.01, 2)] + public void BigDecimalMinScaleShouldProduceExpectedResult(double left, double right, double expected) + { + // When + BigDecimal actual = BigDecimal.MinScale(left, right); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BigDecimal.MaxScale should produce the expected result")] + [InlineData(0, 0, 0)] + [InlineData(0.1, 0, 1)] + [InlineData(0.01, 0.1, 2)] + [InlineData(0.001, 0.01, 3)] + public void BigDecimalMaxScaleShouldProduceExpectedResult(double left, double right, double expected) + { + // When + BigDecimal actual = BigDecimal.MaxScale(left, right); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BigDecimal.MinScaleMaxScale should produce the expected result")] + [InlineData(0, 0, 0, 0)] + [InlineData(0.1, 0, 0, 1)] + [InlineData(0.01, 0.1, 1, 2)] + [InlineData(0.001, 0.01, 2, 3)] + public void BigDecimalMinScaleMaxScaleShouldProduceExpectedResult(double left, double right, double expectedMin, double expectedMax) + { + // When + (BigDecimal actualMin, BigDecimal actualMax) = BigDecimal.MinMaxScale(left, right); + + // Then + Assert.Equal(expectedMin, actualMin); + Assert.Equal(expectedMax, actualMax); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstantTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstantTests.cs new file mode 100644 index 0000000..aa99386 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstantTests.cs @@ -0,0 +1,134 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstantTests +{ + [Fact(DisplayName = "BigDecimal.NegativeOne should produce the expected result")] + public void BigDecimalNegativeOneShouldProduceExpectedResult() + { + // Given + const int expectedUnscaledValue = -1; + const int expectedScale = 0; + + // When + BigDecimal actual = BigDecimal.NegativeOne; + + // Then + Assert.Equal(expectedUnscaledValue, actual.UnscaledValue); + Assert.Equal(expectedScale, actual.Scale); + } + + [Fact(DisplayName = "BigDecimal.Zero should produce the expected result")] + public void BigDecimalZeroShouldProduceExpectedResult() + { + // Given + const int expectedUnscaledValue = 0; + const int expectedScale = 0; + + // When + BigDecimal actual = BigDecimal.Zero; + + // Then + Assert.Equal(expectedUnscaledValue, actual.UnscaledValue); + Assert.Equal(expectedScale, actual.Scale); + } + + [Fact(DisplayName = "BigDecimal.One should produce the expected result")] + public void BigDecimalOneShouldProduceExpectedResult() + { + // Given + const int expectedUnscaledValue = 1; + const int expectedScale = 0; + + // When + BigDecimal actual = BigDecimal.One; + + // Then + Assert.Equal(expectedUnscaledValue, actual.UnscaledValue); + Assert.Equal(expectedScale, actual.Scale); + } + + [Fact(DisplayName = "BigDecimal.Two should produce the expected result")] + public void BigDecimalTwoShouldProduceExpectedResult() + { + // Given + const int expectedUnscaledValue = 2; + const int expectedScale = 0; + + // When + BigDecimal actual = BigDecimal.Two; + + // Then + Assert.Equal(expectedUnscaledValue, actual.UnscaledValue); + Assert.Equal(expectedScale, actual.Scale); + } + + [Fact(DisplayName = "BigDecimal.Ten should produce the expected result")] + public void BigDecimalTenShouldProduceExpectedResult() + { + // Given + const int expectedUnscaledValue = 10; + const int expectedScale = 0; + + // When + BigDecimal actual = BigDecimal.Ten; + + // Then + Assert.Equal(expectedUnscaledValue, actual.UnscaledValue); + Assert.Equal(expectedScale, actual.Scale); + } + + [Fact(DisplayName = "BigDecimal.E should produce the expected result")] + public void BigDecimalEShouldProduceExpectedResult() + { + // Given + const string expected = "2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354"; + + // When + BigDecimal actual = BigDecimal.E; + + // Then + Assert.Equal(expected, actual.ToString()); + } + + [Fact(DisplayName = "BigDecimal.Pi should produce the expected result")] + public void BigDecimalPiShouldProduceExpectedResult() + { + // Given + const string expected = "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989"; + + // When + BigDecimal actual = BigDecimal.Pi; + + // Then + Assert.Equal(expected, actual.ToString()); + } + + [Fact(DisplayName = "BigDecimal.Tau should produce the expected result")] + public void BigDecimalTauShouldProduceExpectedResult() + { + // Given + const string expected = "6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696506842341359642961730265646132941876892191011644634507188162569622349005682054038770422111192892458979098607639288576219513318668922569512964675735663305424038182912971338469206972209086532964267872145204982825474491740132126311763497630418419256585081834307287357851807200226610610976409330427682939038830232188661145407315191839061843722347638652235862102370961489247599254991347037715054497824558763660238982596673467248813132861720427898927904494743814043597218874055410784343525863535047693496369353388102640011362542905271216555715426855155792183472743574429368818024499068602930991707421015845593785178470840399122242580439217280688363196272595495426199210374144226999999967459560999021194634656321926371900489189106938166052850446165066893700705238623763420200062756775057731750664167628412343553382946071965069808575109374623191257277647075751875039155637155610643424536132260038557532223918184328403979"; + + // When + BigDecimal actual = BigDecimal.Tau; + + // Then + Assert.Equal(expected, actual.ToString()); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorBigIntegerTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorBigIntegerTests.cs new file mode 100644 index 0000000..93699e7 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorBigIntegerTests.cs @@ -0,0 +1,76 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorBigIntegerTests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled BigInteger value and scale (fractional scaling)")] + [InlineData(0, 0)] + [InlineData(1, 0)] + [InlineData(1, 1)] + [InlineData(1, 2)] + [InlineData(1, 10)] + [InlineData(-1, 0)] + [InlineData(-1, 1)] + [InlineData(-1, 2)] + [InlineData(-1, 10)] + [InlineData(10, 0)] + [InlineData(10, 1)] + [InlineData(10, 2)] + [InlineData(10, 10)] + [InlineData(-10, 0)] + [InlineData(-10, 1)] + [InlineData(-10, 2)] + [InlineData(-10, 10)] + public void BigDecimalShouldBeConstructableFromUnscaledValueAndScaleWithFractionalScaling(long unscaledValue, int scale) + { + // When + BigDecimal value = unscaledValue.ToBigInteger().ToBigDecimal(scale, ScaleMode.Fractional); + + // Then + Assert.Equal(unscaledValue, value.ToNumberInfo().UnscaledValue); + Assert.Equal(scale, value.ToNumberInfo().Scale); + } + + [Theory(DisplayName = "BigDecimal should be constructable from unscaled BigInteger value and scale (Integral Scaling)")] + [InlineData(0, 0, 0)] + [InlineData(1, 0, 1)] + [InlineData(1, 1, 10)] + [InlineData(1, 2, 100)] + [InlineData(1, 10, 10000000000)] + [InlineData(-1, 0, -1)] + [InlineData(-1, 1, -10)] + [InlineData(-1, 2, -100)] + [InlineData(-1, 10, -10000000000)] + [InlineData(10, 0, 10)] + [InlineData(10, 1, 100)] + [InlineData(10, 2, 1000)] + [InlineData(10, 10, 100000000000)] + [InlineData(-10, 0, -10)] + [InlineData(-10, 1, -100)] + [InlineData(-10, 2, -1000)] + [InlineData(-10, 10, -100000000000)] + public void BigDecimalShouldBeConstructableFromUnscaledValueAndScaleUsingIntegralScaling(long unscaledValue, int scale, long expected) + { + // When + BigDecimal value = unscaledValue.ToBigInteger().ToBigDecimal(scale); + + // Then + Assert.Equal(expected, value.ToNumberInfo().UnscaledValue); + Assert.Equal(scale, value.ToNumberInfo().Scale); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorByteTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorByteTests.cs new file mode 100644 index 0000000..1412c1b --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorByteTests.cs @@ -0,0 +1,59 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public class BigDecimalConstructorByteTests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled byte value and scale (fractional scaling)")] + [InlineData(byte.MaxValue, 0, "255")] + [InlineData(byte.MaxValue, 1, "25.5")] + [InlineData(byte.MaxValue, 2, "2.55")] + [InlineData(byte.MaxValue, 3, "0.255")] + [InlineData(byte.MaxValue, 4, "0.0255")] + [InlineData(byte.MaxValue, 5, "0.00255")] + [InlineData(byte.MaxValue, 6, "0.000255")] + [InlineData(byte.MaxValue, 7, "0.0000255")] + [InlineData(byte.MaxValue, 8, "0.00000255")] + [InlineData(byte.MaxValue, 9, "0.000000255")] + [InlineData(byte.MaxValue, 10, "0.0000000255")] + [InlineData(byte.MaxValue, 20, "0.00000000000000000255")] + [InlineData(byte.MinValue, 0, "0")] + [InlineData(byte.MinValue, 1, "0.0")] + [InlineData(byte.MinValue, 2, "0.00")] + [InlineData(byte.MinValue, 3, "0.000")] + [InlineData(byte.MinValue, 4, "0.0000")] + [InlineData(byte.MinValue, 5, "0.00000")] + [InlineData(byte.MinValue, 6, "0.000000")] + [InlineData(byte.MinValue, 7, "0.0000000")] + [InlineData(byte.MinValue, 8, "0.00000000")] + [InlineData(byte.MinValue, 9, "0.000000000")] + [InlineData(byte.MinValue, 10, "0.0000000000")] + [InlineData(byte.MinValue, 20, "0.00000000000000000000")] + public void BigDecimalShouldBeConstructableFromUnscaledByteValueAndScaleWithFractionalScaling(byte value, int scale, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(scale, ScaleMode.Fractional); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorDoubleBinaryTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorDoubleBinaryTests.cs new file mode 100644 index 0000000..08948eb --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorDoubleBinaryTests.cs @@ -0,0 +1,37 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorDoubleBinaryTests +{ + [BigDecimalIeee754BinaryData] + [Theory(DisplayName = "BigDecimal should be constructable from double value using Binary conversion")] + public void BigDecimalShouldBeConstructableFromDoubleValueUsingBinaryConversion(double value, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(ConversionMode.Binary); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorDoubleDecimalTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorDoubleDecimalTests.cs new file mode 100644 index 0000000..78255ca --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorDoubleDecimalTests.cs @@ -0,0 +1,37 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorDoubleDecimalTests +{ + [BigDecimalIeee754DecimalData] + [Theory(DisplayName = "BigDecimal should be constructable from double value using Decimal conversion")] + public void BigDecimalShouldBeConstructableFromDoubleValueUsingDecimalConversion(double value, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt16Tests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt16Tests.cs new file mode 100644 index 0000000..68a2e60 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt16Tests.cs @@ -0,0 +1,59 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public class BigDecimalConstructorInt16Tests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled Int16 value and scale (fractional scaling)")] + [InlineData(short.MaxValue, 0, "32767")] + [InlineData(short.MaxValue, 1, "3276.7")] + [InlineData(short.MaxValue, 2, "327.67")] + [InlineData(short.MaxValue, 3, "32.767")] + [InlineData(short.MaxValue, 4, "3.2767")] + [InlineData(short.MaxValue, 5, "0.32767")] + [InlineData(short.MaxValue, 6, "0.032767")] + [InlineData(short.MaxValue, 7, "0.0032767")] + [InlineData(short.MaxValue, 8, "0.00032767")] + [InlineData(short.MaxValue, 9, "0.000032767")] + [InlineData(short.MaxValue, 10, "0.0000032767")] + [InlineData(short.MaxValue, 20, "0.00000000000000032767")] + [InlineData(short.MinValue, 0, "-32768")] + [InlineData(short.MinValue, 1, "-3276.8")] + [InlineData(short.MinValue, 2, "-327.68")] + [InlineData(short.MinValue, 3, "-32.768")] + [InlineData(short.MinValue, 4, "-3.2768")] + [InlineData(short.MinValue, 5, "-0.32768")] + [InlineData(short.MinValue, 6, "-0.032768")] + [InlineData(short.MinValue, 7, "-0.0032768")] + [InlineData(short.MinValue, 8, "-0.00032768")] + [InlineData(short.MinValue, 9, "-0.000032768")] + [InlineData(short.MinValue, 10, "-0.0000032768")] + [InlineData(short.MinValue, 20, "-0.00000000000000032768")] + public void BigDecimalShouldBeConstructableFromUnscaledInt16ValueAndScaleWithFractionalScaling(short value, int scale, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(scale, ScaleMode.Fractional); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt32Tests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt32Tests.cs new file mode 100644 index 0000000..07c0395 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt32Tests.cs @@ -0,0 +1,83 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorInt32Tests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled Int32 value and scale (fractional scaling)")] + [InlineData(1, 0, "1")] + [InlineData(1, 1, "0.1")] + [InlineData(1, 2, "0.01")] + [InlineData(1, 3, "0.001")] + [InlineData(1, 4, "0.0001")] + [InlineData(1, 5, "0.00001")] + [InlineData(1, 6, "0.000001")] + [InlineData(1, 7, "0.0000001")] + [InlineData(1, 8, "0.00000001")] + [InlineData(1, 9, "0.000000001")] + [InlineData(1, 10, "0.0000000001")] + [InlineData(1, 20, "0.00000000000000000001")] + [InlineData(-1, 0, "-1")] + [InlineData(-1, 1, "-0.1")] + [InlineData(-1, 2, "-0.01")] + [InlineData(-1, 3, "-0.001")] + [InlineData(-1, 4, "-0.0001")] + [InlineData(-1, 5, "-0.00001")] + [InlineData(-1, 6, "-0.000001")] + [InlineData(-1, 7, "-0.0000001")] + [InlineData(-1, 8, "-0.00000001")] + [InlineData(-1, 9, "-0.000000001")] + [InlineData(-1, 10, "-0.0000000001")] + [InlineData(-1, 20, "-0.00000000000000000001")] + [InlineData(int.MaxValue, 0, "2147483647")] + [InlineData(int.MaxValue, 1, "214748364.7")] + [InlineData(int.MaxValue, 2, "21474836.47")] + [InlineData(int.MaxValue, 3, "2147483.647")] + [InlineData(int.MaxValue, 4, "214748.3647")] + [InlineData(int.MaxValue, 5, "21474.83647")] + [InlineData(int.MaxValue, 6, "2147.483647")] + [InlineData(int.MaxValue, 7, "214.7483647")] + [InlineData(int.MaxValue, 8, "21.47483647")] + [InlineData(int.MaxValue, 9, "2.147483647")] + [InlineData(int.MaxValue, 10, "0.2147483647")] + [InlineData(int.MaxValue, 20, "0.00000000002147483647")] + [InlineData(int.MinValue, 0, "-2147483648")] + [InlineData(int.MinValue, 1, "-214748364.8")] + [InlineData(int.MinValue, 2, "-21474836.48")] + [InlineData(int.MinValue, 3, "-2147483.648")] + [InlineData(int.MinValue, 4, "-214748.3648")] + [InlineData(int.MinValue, 5, "-21474.83648")] + [InlineData(int.MinValue, 6, "-2147.483648")] + [InlineData(int.MinValue, 7, "-214.7483648")] + [InlineData(int.MinValue, 8, "-21.47483648")] + [InlineData(int.MinValue, 9, "-2.147483648")] + [InlineData(int.MinValue, 10, "-0.2147483648")] + [InlineData(int.MinValue, 20, "-0.00000000002147483648")] + public void BigDecimalShouldBeConstructableFromUnscaledInt32ValueAndScaleWithFractionalScaling(int value, int scale, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(scale, ScaleMode.Fractional); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt64Tests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt64Tests.cs new file mode 100644 index 0000000..bc1c254 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorInt64Tests.cs @@ -0,0 +1,59 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorInt64Tests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled Int64 value and scale (fractional scaling)")] + [InlineData(long.MaxValue, 0, "9223372036854775807")] + [InlineData(long.MaxValue, 1, "922337203685477580.7")] + [InlineData(long.MaxValue, 2, "92233720368547758.07")] + [InlineData(long.MaxValue, 3, "9223372036854775.807")] + [InlineData(long.MaxValue, 4, "922337203685477.5807")] + [InlineData(long.MaxValue, 5, "92233720368547.75807")] + [InlineData(long.MaxValue, 6, "9223372036854.775807")] + [InlineData(long.MaxValue, 7, "922337203685.4775807")] + [InlineData(long.MaxValue, 8, "92233720368.54775807")] + [InlineData(long.MaxValue, 9, "9223372036.854775807")] + [InlineData(long.MaxValue, 10, "922337203.6854775807")] + [InlineData(long.MaxValue, 20, "0.09223372036854775807")] + [InlineData(long.MinValue, 0, "-9223372036854775808")] + [InlineData(long.MinValue, 1, "-922337203685477580.8")] + [InlineData(long.MinValue, 2, "-92233720368547758.08")] + [InlineData(long.MinValue, 3, "-9223372036854775.808")] + [InlineData(long.MinValue, 4, "-922337203685477.5808")] + [InlineData(long.MinValue, 5, "-92233720368547.75808")] + [InlineData(long.MinValue, 6, "-9223372036854.775808")] + [InlineData(long.MinValue, 7, "-922337203685.4775808")] + [InlineData(long.MinValue, 8, "-92233720368.54775808")] + [InlineData(long.MinValue, 9, "-9223372036.854775808")] + [InlineData(long.MinValue, 10, "-922337203.6854775808")] + [InlineData(long.MinValue, 20, "-0.09223372036854775808")] + public void BigDecimalShouldBeConstructableFromUnscaledInt64ValueAndScaleWithFractionalScaling(long value, int scale, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(scale, ScaleMode.Fractional); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorSByteTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorSByteTests.cs new file mode 100644 index 0000000..7ea0eb1 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorSByteTests.cs @@ -0,0 +1,59 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorSByteTests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled sbyte value and scale (fractional scaling)")] + [InlineData(sbyte.MaxValue, 0, "127")] + [InlineData(sbyte.MaxValue, 1, "12.7")] + [InlineData(sbyte.MaxValue, 2, "1.27")] + [InlineData(sbyte.MaxValue, 3, "0.127")] + [InlineData(sbyte.MaxValue, 4, "0.0127")] + [InlineData(sbyte.MaxValue, 5, "0.00127")] + [InlineData(sbyte.MaxValue, 6, "0.000127")] + [InlineData(sbyte.MaxValue, 7, "0.0000127")] + [InlineData(sbyte.MaxValue, 8, "0.00000127")] + [InlineData(sbyte.MaxValue, 9, "0.000000127")] + [InlineData(sbyte.MaxValue, 10, "0.0000000127")] + [InlineData(sbyte.MaxValue, 20, "0.00000000000000000127")] + [InlineData(sbyte.MinValue, 0, "-128")] + [InlineData(sbyte.MinValue, 1, "-12.8")] + [InlineData(sbyte.MinValue, 2, "-1.28")] + [InlineData(sbyte.MinValue, 3, "-0.128")] + [InlineData(sbyte.MinValue, 4, "-0.0128")] + [InlineData(sbyte.MinValue, 5, "-0.00128")] + [InlineData(sbyte.MinValue, 6, "-0.000128")] + [InlineData(sbyte.MinValue, 7, "-0.0000128")] + [InlineData(sbyte.MinValue, 8, "-0.00000128")] + [InlineData(sbyte.MinValue, 9, "-0.000000128")] + [InlineData(sbyte.MinValue, 10, "-0.0000000128")] + [InlineData(sbyte.MinValue, 20, "-0.00000000000000000128")] + public void BigDecimalShouldBeConstructableFromUnscaledSByteValueAndScaleWithFractionalScaling(sbyte value, int scale, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(scale, ScaleMode.Fractional); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt16Tests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt16Tests.cs new file mode 100644 index 0000000..9e61076 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt16Tests.cs @@ -0,0 +1,59 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorUInt16Tests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled UInt16 value and scale (fractional scaling)")] + [InlineData(ushort.MaxValue, 0, "65535")] + [InlineData(ushort.MaxValue, 1, "6553.5")] + [InlineData(ushort.MaxValue, 2, "655.35")] + [InlineData(ushort.MaxValue, 3, "65.535")] + [InlineData(ushort.MaxValue, 4, "6.5535")] + [InlineData(ushort.MaxValue, 5, "0.65535")] + [InlineData(ushort.MaxValue, 6, "0.065535")] + [InlineData(ushort.MaxValue, 7, "0.0065535")] + [InlineData(ushort.MaxValue, 8, "0.00065535")] + [InlineData(ushort.MaxValue, 9, "0.000065535")] + [InlineData(ushort.MaxValue, 10, "0.0000065535")] + [InlineData(ushort.MaxValue, 20, "0.00000000000000065535")] + [InlineData(ushort.MinValue, 0, "0")] + [InlineData(ushort.MinValue, 1, "0.0")] + [InlineData(ushort.MinValue, 2, "0.00")] + [InlineData(ushort.MinValue, 3, "0.000")] + [InlineData(ushort.MinValue, 4, "0.0000")] + [InlineData(ushort.MinValue, 5, "0.00000")] + [InlineData(ushort.MinValue, 6, "0.000000")] + [InlineData(ushort.MinValue, 7, "0.0000000")] + [InlineData(ushort.MinValue, 8, "0.00000000")] + [InlineData(ushort.MinValue, 9, "0.000000000")] + [InlineData(ushort.MinValue, 10, "0.0000000000")] + [InlineData(ushort.MinValue, 20, "0.00000000000000000000")] + public void BigDecimalShouldBeConstructableFromUnscaledUInt16ValueAndScaleWithFractionalScaling(ushort value, int scale, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(scale, ScaleMode.Fractional); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt32Tests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt32Tests.cs new file mode 100644 index 0000000..6a70a9c --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt32Tests.cs @@ -0,0 +1,59 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorUInt32Tests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled UInt32 value and scale (fractional scaling)")] + [InlineData(uint.MaxValue, 0, "4294967295")] + [InlineData(uint.MaxValue, 1, "429496729.5")] + [InlineData(uint.MaxValue, 2, "42949672.95")] + [InlineData(uint.MaxValue, 3, "4294967.295")] + [InlineData(uint.MaxValue, 4, "429496.7295")] + [InlineData(uint.MaxValue, 5, "42949.67295")] + [InlineData(uint.MaxValue, 6, "4294.967295")] + [InlineData(uint.MaxValue, 7, "429.4967295")] + [InlineData(uint.MaxValue, 8, "42.94967295")] + [InlineData(uint.MaxValue, 9, "4.294967295")] + [InlineData(uint.MaxValue, 10, "0.4294967295")] + [InlineData(uint.MaxValue, 20, "0.00000000004294967295")] + [InlineData(uint.MinValue, 0, "0")] + [InlineData(uint.MinValue, 1, "0.0")] + [InlineData(uint.MinValue, 2, "0.00")] + [InlineData(uint.MinValue, 3, "0.000")] + [InlineData(uint.MinValue, 4, "0.0000")] + [InlineData(uint.MinValue, 5, "0.00000")] + [InlineData(uint.MinValue, 6, "0.000000")] + [InlineData(uint.MinValue, 7, "0.0000000")] + [InlineData(uint.MinValue, 8, "0.00000000")] + [InlineData(uint.MinValue, 9, "0.000000000")] + [InlineData(uint.MinValue, 10, "0.0000000000")] + [InlineData(uint.MinValue, 20, "0.00000000000000000000")] + public void BigDecimalShouldBeConstructableFromUnscaledUInt32ValueAndScaleWithFractionalScaling(uint value, int scale, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(scale, ScaleMode.Fractional); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt64Tests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt64Tests.cs new file mode 100644 index 0000000..b6ce6c1 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalConstructorUInt64Tests.cs @@ -0,0 +1,59 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalConstructorUInt64Tests +{ + [Theory(DisplayName = "BigDecimal should be constructable from unscaled UInt64 value and scale (fractional scaling)")] + [InlineData(ulong.MaxValue, 0, "18446744073709551615")] + [InlineData(ulong.MaxValue, 1, "1844674407370955161.5")] + [InlineData(ulong.MaxValue, 2, "184467440737095516.15")] + [InlineData(ulong.MaxValue, 3, "18446744073709551.615")] + [InlineData(ulong.MaxValue, 4, "1844674407370955.1615")] + [InlineData(ulong.MaxValue, 5, "184467440737095.51615")] + [InlineData(ulong.MaxValue, 6, "18446744073709.551615")] + [InlineData(ulong.MaxValue, 7, "1844674407370.9551615")] + [InlineData(ulong.MaxValue, 8, "184467440737.09551615")] + [InlineData(ulong.MaxValue, 9, "18446744073.709551615")] + [InlineData(ulong.MaxValue, 10, "1844674407.3709551615")] + [InlineData(ulong.MaxValue, 20, "0.18446744073709551615")] + [InlineData(ulong.MinValue, 0, "0")] + [InlineData(ulong.MinValue, 1, "0.0")] + [InlineData(ulong.MinValue, 2, "0.00")] + [InlineData(ulong.MinValue, 3, "0.000")] + [InlineData(ulong.MinValue, 4, "0.0000")] + [InlineData(ulong.MinValue, 5, "0.00000")] + [InlineData(ulong.MinValue, 6, "0.000000")] + [InlineData(ulong.MinValue, 7, "0.0000000")] + [InlineData(ulong.MinValue, 8, "0.00000000")] + [InlineData(ulong.MinValue, 9, "0.000000000")] + [InlineData(ulong.MinValue, 10, "0.0000000000")] + [InlineData(ulong.MinValue, 20, "0.00000000000000000000")] + public void BigDecimalShouldBeConstructableFromUnscaledUInt64ValueAndScaleWithFractionalScaling(ulong value, int scale, string expected) + { + // Given + BigDecimal candidate = value.ToBigDecimal(scale, ScaleMode.Fractional); + CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalEqualityComparerTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalEqualityComparerTests.cs new file mode 100644 index 0000000..757cfca --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalEqualityComparerTests.cs @@ -0,0 +1,57 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalEqualityComparerTests +{ + [NumberInfoEqualityComparerData] + [Theory(DisplayName = "BigDecimalEqualityComparer.Equals should produce the expected result (Strict)")] + public void BigDecimalEqualityComparerEqualsShouldProduceExpectedResultStrict(decimal left, decimal right, Guid _) + { + // Given + bool expected = left == right && left.Scale == right.Scale; + BigDecimal leftCandidate = left.ToBigDecimal(); + BigDecimal rightCandidate = right.ToBigDecimal(); + + // When + bool actualFromComparer = BigDecimalEqualityComparer.Strict.Equals(leftCandidate, rightCandidate); + bool actualFromBigDecimal = leftCandidate.Equals(rightCandidate); + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromBigDecimal); + } + + [NumberInfoEqualityComparerData] + [Theory(DisplayName = "BigDecimalEqualityComparer.Equals should produce the expected result (Semantic)")] + public void BigDecimalEqualityComparerEqualsShouldProduceExpectedResultSemantic(decimal left, decimal right, Guid _) + { + // Given + bool expected = left == right; + BigDecimal leftCandidate = left.ToBigDecimal(); + BigDecimal rightCandidate = right.ToBigDecimal(); + + // When + bool actualFromComparer = BigDecimalEqualityComparer.Semantic.Equals(leftCandidate, rightCandidate); + bool actualFromBigDecimal = leftCandidate == rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromBigDecimal); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalFormatTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalFormatTests.cs new file mode 100644 index 0000000..388556a --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalFormatTests.cs @@ -0,0 +1,97 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalFormatTests +{ + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Format should produce the expected result (Currency)")] + public void BigDecimalFormatShouldProduceExpectedResultCurrency(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = string.Create(culture, $"{value:C}"); + BigDecimal candidate = value; + + // When + string actual = string.Create(culture, $"{candidate:C}"); + + // Then + Assert.Equal(expected, actual); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Format should produce the expected result (Fixed)")] + public void BigDecimalFormatShouldProduceExpectedResultFixed(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = string.Create(culture, $"{value:F}"); + BigDecimal candidate = value; + + // When + string actual = string.Create(culture, $"{candidate:F}"); + + // Then + Assert.Equal(expected, actual); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Format should produce the expected result (General)")] + public void BigDecimalFormatShouldProduceExpectedResultGeneral(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = string.Create(culture, $"{value:G}"); + BigDecimal candidate = value; + + // When + string actual = string.Create(culture, $"{candidate:G}"); + + // Then + Assert.Equal(expected, actual); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Format should produce the expected result (Number)")] + public void BigDecimalFormatShouldProduceExpectedResultNumber(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = string.Create(culture, $"{value:N}"); + BigDecimal candidate = value; + + // When + string actual = string.Create(culture, $"{candidate:N}"); + + // Then + Assert.Equal(expected, actual); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Format should produce the expected result (Percent)")] + public void BigDecimalFormatShouldProduceExpectedResultPercent(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = string.Create(culture, $"{value:P}"); + BigDecimal candidate = value; + + // When + string actual = string.Create(culture, $"{candidate:P}"); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalIsTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalIsTests.cs new file mode 100644 index 0000000..4ecbf3c --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalIsTests.cs @@ -0,0 +1,105 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalIsTests +{ + [BigDecimalIsData] + [Theory(DisplayName = "BigDecimal.IsInteger should produce the expected result")] + public void BigDecimalIsIntegerShouldProduceExpectedResult(decimal value, Guid _) + { + // Given + bool expected = decimal.IsInteger(value); + + // When + bool actual = BigDecimal.IsInteger(value); + + // Then + Assert.Equal(expected, actual); + } + + [BigDecimalIsData] + [Theory(DisplayName = "BigDecimal.IsEvenInteger should produce the expected result")] + public void BigDecimalIsEvenIntegerShouldProduceExpectedResult(decimal value, Guid _) + { + // Given + bool expected = decimal.IsEvenInteger(value); + + // When + bool actual = BigDecimal.IsEvenInteger(value); + + // Then + Assert.Equal(expected, actual); + } + + [BigDecimalIsData] + [Theory(DisplayName = "BigDecimal.IsOddInteger should produce the expected result")] + public void BigDecimalIsOddIntegerShouldProduceExpectedResult(decimal value, Guid _) + { + // Given + bool expected = decimal.IsOddInteger(value); + + // When + bool actual = BigDecimal.IsOddInteger(value); + + // Then + Assert.Equal(expected, actual); + } + + [BigDecimalIsData] + [Theory(DisplayName = "BigDecimal.IsNegative should produce the expected result")] + public void BigDecimalIsNegativeShouldProduceExpectedResult(decimal value, Guid _) + { + // Given + bool expected = decimal.IsNegative(value); + + // When + bool actual = BigDecimal.IsNegative(value); + + // Then + Assert.Equal(expected, actual); + } + + [BigDecimalIsData] + [Theory(DisplayName = "BigDecimal.IsPositive should produce the expected result")] + public void BigDecimalIsPositiveShouldProduceExpectedResult(decimal value, Guid _) + { + // Given + bool expected = decimal.IsPositive(value); + + // When + bool actual = BigDecimal.IsPositive(value); + + // Then + Assert.Equal(expected, actual); + } + + [BigDecimalIsData] + [Theory(DisplayName = "BigDecimal.IsZero should produce the expected result")] + public void BigDecimalIsZeroShouldProduceExpectedResult(decimal value, Guid _) + { + // Given + bool expected = value is 0; + + // When + bool actual = BigDecimal.IsZero(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalOrdinalityComparerTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalOrdinalityComparerTests.cs new file mode 100644 index 0000000..91f1f04 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalOrdinalityComparerTests.cs @@ -0,0 +1,129 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalOrdinalityComparerTests +{ + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "BigDecimalOrdinalityComparer.Compare should produce the expected result")] + public void BigDecimalOrdinalityComparerCompareShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + int expected = left.CompareTo(right); + BigDecimal leftCandidate = left.ToBigDecimal(); + BigDecimal rightCandidate = right.ToBigDecimal(); + + // When + int actualFromComparer = BigDecimalOrdinalityComparer.Default.Compare(leftCandidate, rightCandidate); + int actualFromBigDecimal = leftCandidate.CompareTo(rightCandidate); + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromBigDecimal); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "BigDecimalOrdinalityComparer.IsEqual should produce the expected result")] + public void BigDecimalOrdinalityComparerIsEqualShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left.CompareTo(right) is 0; + BigDecimal leftCandidate = left.ToBigDecimal(); + BigDecimal rightCandidate = right.ToBigDecimal(); + + // When + bool actualFromComparer = BigDecimalOrdinalityComparer.Default.IsEqual(leftCandidate, rightCandidate); + bool actualFromBigDecimal = leftCandidate.CompareTo(rightCandidate) is 0; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromBigDecimal); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "BigDecimalOrdinalityComparer.IsGreaterThan should produce the expected result")] + public void BigDecimalOrdinalityComparerIsGreaterThanShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left > right; + BigDecimal leftCandidate = left.ToBigDecimal(); + BigDecimal rightCandidate = right.ToBigDecimal(); + + // When + bool actualFromComparer = BigDecimalOrdinalityComparer.Default.IsGreaterThan(leftCandidate, rightCandidate); + bool actualFromBigDecimal = leftCandidate > rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromBigDecimal); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "BigDecimalOrdinalityComparer.IsGreaterThanOrEqual should produce the expected result")] + public void BigDecimalOrdinalityComparerIsGreaterOrEqualThanShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left >= right; + BigDecimal leftCandidate = left.ToBigDecimal(); + BigDecimal rightCandidate = right.ToBigDecimal(); + + // When + bool actualFromComparer = BigDecimalOrdinalityComparer.Default.IsGreaterThanOrEqual(leftCandidate, rightCandidate); + bool actualFromBigDecimal = leftCandidate >= rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromBigDecimal); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "BigDecimalOrdinalityComparer.IsLessThan should produce the expected result")] + public void BigDecimalOrdinalityComparerIsLessThanShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left < right; + BigDecimal leftCandidate = left.ToBigDecimal(); + BigDecimal rightCandidate = right.ToBigDecimal(); + + // When + bool actualFromComparer = BigDecimalOrdinalityComparer.Default.IsLessThan(leftCandidate, rightCandidate); + bool actualFromBigDecimal = leftCandidate < rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromBigDecimal); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "BigDecimalOrdinalityComparer.IsLessThanOrEqual should produce the expected result")] + public void BigDecimalOrdinalityComparerIsLessOrEqualThanShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left <= right; + BigDecimal leftCandidate = left.ToBigDecimal(); + BigDecimal rightCandidate = right.ToBigDecimal(); + + // When + bool actualFromComparer = BigDecimalOrdinalityComparer.Default.IsLessThanOrEqual(leftCandidate, rightCandidate); + bool actualFromBigDecimal = leftCandidate <= rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromBigDecimal); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalParseTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalParseTests.cs new file mode 100644 index 0000000..ddb774a --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalParseTests.cs @@ -0,0 +1,96 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalParseTests +{ + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Parse should produce the expected result (Currency)")] + public void BigDecimalParseShouldProduceExpectedResultCurrency(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToString("C", culture); + decimal expected = decimal.Parse(formatted, NumberStyles.Currency, culture); + + // When + BigDecimal actual = BigDecimal.Parse(formatted, NumberStyles.Currency, culture); + + // Then + Assert.Equal(expected, actual, BigDecimalEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Parse should produce the expected result (Decimal)")] + public void BigDecimalParseShouldProduceExpectedResultDecimal(decimal expected, CultureInfo culture, Guid _) + { + // Given + string formatted = expected.ToBigDecimal().ToString("D", culture); + + // When + BigDecimal actual = BigDecimal.Parse(formatted, NumberStyles.Number, culture); + + // Then + Assert.Equal(expected, actual, BigDecimalEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Parse should produce the expected result (Fixed)")] + public void BigDecimalParseShouldProduceExpectedResultFixed(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToString("F", culture); + decimal expected = decimal.Parse(formatted, NumberStyles.Float, culture); + + // When + BigDecimal actual = BigDecimal.Parse(formatted, NumberStyles.Float, culture); + + // Then + Assert.Equal(expected, actual, BigDecimalEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Parse should produce the expected result (General)")] + public void BigDecimalParseShouldProduceExpectedResultGeneral(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToString("G", culture); + decimal expected = decimal.Parse(formatted, NumberStyles.Any, culture); + + // When + BigDecimal actual = BigDecimal.Parse(formatted, NumberStyles.Any, culture); + + // Then + Assert.Equal(expected, actual, BigDecimalEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.Parse should produce the expected result (Number)")] + public void BigDecimalParseShouldProduceExpectedResultNumber(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToString("G", culture); + decimal expected = decimal.Parse(formatted, NumberStyles.Number, culture); + + // When + BigDecimal actual = BigDecimal.Parse(formatted, NumberStyles.Number, culture); + + // Then + Assert.Equal(expected, actual, BigDecimalEqualityComparer.Semantic); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalToStringTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalToStringTests.cs new file mode 100644 index 0000000..b8f12c3 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalToStringTests.cs @@ -0,0 +1,97 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalToStringTests +{ + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.ToString should produce the expected result (Currency)")] + public void BigDecimalToStringShouldProduceExpectedResultCurrency(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = value.ToString("C", culture); + BigDecimal candidate = value; + + // When + string actual = candidate.ToString("C", culture); + + // Then + Assert.Equal(expected, actual); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.ToString should produce the expected result (Fixed)")] + public void BigDecimalToStringShouldProduceExpectedResultFixed(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = value.ToString("F", culture); + BigDecimal candidate = value; + + // When + string actual = candidate.ToString("F", culture); + + // Then + Assert.Equal(expected, actual); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.ToString should produce the expected result (General)")] + public void BigDecimalToStringShouldProduceExpectedResultGeneral(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = value.ToString("G", culture); + BigDecimal candidate = value; + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.ToString should produce the expected result (Number)")] + public void BigDecimalToStringShouldProduceExpectedResultNumber(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = value.ToString("N", culture); + BigDecimal candidate = value; + + // When + string actual = candidate.ToString("N", culture); + + // Then + Assert.Equal(expected, actual); + } + + [NumberFormatData] + [Theory(DisplayName = "BigDecimal.ToString should produce the expected result (Percent)")] + public void BigDecimalToStringShouldProduceExpectedResultPercent(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = value.ToString("P", culture); + BigDecimal candidate = value; + + // When + string actual = candidate.ToString("P", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/BigDecimalWriteTests.cs b/OnixLabs.Numerics.UnitTests/BigDecimalWriteTests.cs new file mode 100644 index 0000000..9950022 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/BigDecimalWriteTests.cs @@ -0,0 +1,129 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Buffers.Binary; +using System.Numerics; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class BigDecimalWriteTests +{ + [BigDecimalWriteData] + [Theory(DisplayName = "BigDecimal write significand big endian should produce expected result.")] + public void BigDecimalWriteSignificandBigEndianShouldProduceExpectedResult(decimal value) + { + // Given + NumberInfo info = value.ToBigDecimal().ToNumberInfo(); + byte[] significandBytes = new byte[info.Significand.ToByteArray().Length]; + + // When + ((IFloatingPoint)(BigDecimal)value).WriteSignificandBigEndian(significandBytes); + BigInteger significand = new(significandBytes, isBigEndian: true); + + // Then + Assert.Equal(info.Significand, significand); + } + + [BigDecimalWriteData] + [Theory(DisplayName = "BigDecimal write exponent big endian should produce expected result.")] + public void BigDecimalWriteExponentBigEndianShouldProduceExpectedResult(decimal value) + { + // Given + NumberInfo info = value.ToBigDecimal().ToNumberInfo(); + byte[] exponentBytes = new byte[4]; + + // When + ((IFloatingPoint)(BigDecimal)value).WriteExponentBigEndian(exponentBytes); + int exponent = BinaryPrimitives.ReadInt32BigEndian(exponentBytes); + + // Then + Assert.Equal(info.Exponent, exponent); + } + + [BigDecimalWriteData] + [Theory(DisplayName = "BigDecimal write big endian significand and exponent should produce expected result.")] + public void BigDecimalWriteBigEndianSignificandAndExponentShouldProduceExpectedResult(decimal value) + { + // Given + NumberInfo info = value.ToBigDecimal().ToNumberInfo(); + byte[] significandBytes = new byte[info.Significand.ToByteArray().Length]; + byte[] exponentBytes = new byte[4]; + + // When + ((IFloatingPoint)(BigDecimal)value).WriteSignificandBigEndian(significandBytes); + ((IFloatingPoint)(BigDecimal)value).WriteExponentBigEndian(exponentBytes); + + BigInteger significand = new(significandBytes, isBigEndian: true); + int exponent = BinaryPrimitives.ReadInt32BigEndian(exponentBytes); + + // Then + Assert.Equal(info.Significand, significand); + Assert.Equal(info.Exponent, exponent); + } + + [BigDecimalWriteData] + [Theory(DisplayName = "BigDecimal write significand little endian should produce expected result.")] + public void BigDecimalWriteSignificandLittleEndianShouldProduceExpectedResult(decimal value) + { + // Given + NumberInfo info = value.ToBigDecimal().ToNumberInfo(); + byte[] significandBytes = new byte[info.Significand.ToByteArray().Length]; + + // When + ((IFloatingPoint)(BigDecimal)value).WriteSignificandLittleEndian(significandBytes); + BigInteger significand = new(significandBytes); + + // Then + Assert.Equal(info.Significand, significand); + } + + [BigDecimalWriteData] + [Theory(DisplayName = "BigDecimal write exponent little endian should produce expected result.")] + public void BigDecimalWriteExponentLittleEndianShouldProduceExpectedResult(decimal value) + { + // Given + NumberInfo info = value.ToBigDecimal().ToNumberInfo(); + byte[] exponentBytes = new byte[4]; + + // When + ((IFloatingPoint)(BigDecimal)value).WriteExponentLittleEndian(exponentBytes); + int exponent = BinaryPrimitives.ReadInt32LittleEndian(exponentBytes); + + // Then + Assert.Equal(info.Exponent, exponent); + } + + [BigDecimalWriteData] + [Theory(DisplayName = "BigDecimal write little endian significand and exponent should produce expected result.")] + public void BigDecimalWriteLittleEndianSignificandAndExponentShouldProduceExpectedResult(decimal value) + { + // Given + NumberInfo info = value.ToBigDecimal().ToNumberInfo(); + byte[] significandBytes = new byte[info.Significand.ToByteArray().Length]; + byte[] exponentBytes = new byte[4]; + + // When + ((IFloatingPoint)(BigDecimal)value).WriteSignificandLittleEndian(significandBytes); + ((IFloatingPoint)(BigDecimal)value).WriteExponentLittleEndian(exponentBytes); + + BigInteger significand = new(significandBytes); + int exponent = BinaryPrimitives.ReadInt32LittleEndian(exponentBytes); + + // Then + Assert.Equal(info.Significand, significand); + Assert.Equal(info.Exponent, exponent); + } +} diff --git a/OnixLabs.Numerics.UnitTests/GenericMathDeltaTests.cs b/OnixLabs.Numerics.UnitTests/GenericMathDeltaTests.cs new file mode 100644 index 0000000..db671c3 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/GenericMathDeltaTests.cs @@ -0,0 +1,67 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class GenericMathDeltaTests +{ + [Theory(DisplayName = "GenericMath.Delta should produce the expected result")] + [InlineData(0, 0, 0)] + [InlineData(0, 1, 1)] + [InlineData(0, -1, 1)] + [InlineData(10, 0, 10)] + [InlineData(10, 10, 0)] + [InlineData(10, 9, 1)] + [InlineData(10, 8, 2)] + [InlineData(10, 7, 3)] + [InlineData(10, 6, 4)] + [InlineData(10, 5, 5)] + [InlineData(10, 4, 6)] + [InlineData(10, 3, 7)] + [InlineData(10, 2, 8)] + [InlineData(10, 1, 9)] + [InlineData(-10, 10, 20)] + [InlineData(-10, 9, 19)] + [InlineData(-10, 8, 18)] + [InlineData(-10, 7, 17)] + [InlineData(-10, 6, 16)] + [InlineData(-10, 5, 15)] + [InlineData(-10, 4, 14)] + [InlineData(-10, 3, 13)] + [InlineData(-10, 2, 12)] + [InlineData(-10, 1, 11)] + [InlineData(-10, 0, 10)] + [InlineData(1, 0.1, 0.9)] + [InlineData(1, 0.01, 0.99)] + [InlineData(1, 0.001, 0.999)] + [InlineData(10.125, 0.1, 10.025)] + [InlineData(10.125, 0.01, 10.115)] + [InlineData(10.125, 0.001, 10.124)] + [InlineData(-1, 0.1, 1.1)] + [InlineData(-1, 0.01, 1.01)] + [InlineData(-1, 0.001, 1.001)] + [InlineData(-10.125, 0.1, 10.225)] + [InlineData(-10.125, 0.01, 10.135)] + [InlineData(-10.125, 0.001, 10.126)] + public void GenericMathDeltaShouldProduceExpectedResult(double left, double right, double expected) + { + // When + double actual = GenericMath.Delta(left, right); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/GenericMathIntegerLengthTests.cs b/OnixLabs.Numerics.UnitTests/GenericMathIntegerLengthTests.cs new file mode 100644 index 0000000..b7fb89a --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/GenericMathIntegerLengthTests.cs @@ -0,0 +1,269 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class GenericMathIntegerLengthTests +{ + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (SByte)")] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 2)] + [InlineData(100, 3)] + [InlineData(-1, 1)] + [InlineData(-10, 2)] + [InlineData(-100, 3)] + [InlineData(sbyte.MinValue, 3)] + [InlineData(sbyte.MaxValue, 3)] + public void GenericMathIntegerLengthShouldProduceExpectedResultSByte(sbyte value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (Byte)")] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 2)] + [InlineData(100, 3)] + [InlineData(byte.MinValue, 1)] + [InlineData(byte.MaxValue, 3)] + public void GenericMathIntegerLengthShouldProduceExpectedResultByte(byte value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (Int16)")] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 2)] + [InlineData(100, 3)] + [InlineData(1000, 4)] + [InlineData(10000, 5)] + [InlineData(-1, 1)] + [InlineData(-10, 2)] + [InlineData(-100, 3)] + [InlineData(-1000, 4)] + [InlineData(-10000, 5)] + [InlineData(short.MinValue, 5)] + [InlineData(short.MaxValue, 5)] + public void GenericMathIntegerLengthShouldProduceExpectedResultInt16(short value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (UInt16)")] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 2)] + [InlineData(100, 3)] + [InlineData(1000, 4)] + [InlineData(10000, 5)] + [InlineData(ushort.MinValue, 1)] + [InlineData(ushort.MaxValue, 5)] + public void GenericMathIntegerLengthShouldProduceExpectedResultUInt16(ushort value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (Int32)")] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 2)] + [InlineData(100, 3)] + [InlineData(1000, 4)] + [InlineData(10000, 5)] + [InlineData(100000, 6)] + [InlineData(1000000, 7)] + [InlineData(10000000, 8)] + [InlineData(100000000, 9)] + [InlineData(1000000000, 10)] + [InlineData(-1, 1)] + [InlineData(-10, 2)] + [InlineData(-100, 3)] + [InlineData(-1000, 4)] + [InlineData(-10000, 5)] + [InlineData(-100000, 6)] + [InlineData(-1000000, 7)] + [InlineData(-10000000, 8)] + [InlineData(-100000000, 9)] + [InlineData(-1000000000, 10)] + [InlineData(int.MinValue, 10)] + [InlineData(int.MaxValue, 10)] + public void GenericMathIntegerLengthShouldProduceExpectedResultInt32(int value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (UInt32)")] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 2)] + [InlineData(100, 3)] + [InlineData(1000, 4)] + [InlineData(10000, 5)] + [InlineData(100000, 6)] + [InlineData(1000000, 7)] + [InlineData(10000000, 8)] + [InlineData(100000000, 9)] + [InlineData(1000000000, 10)] + [InlineData(uint.MinValue, 1)] + [InlineData(uint.MaxValue, 10)] + public void GenericMathIntegerLengthShouldProduceExpectedResultUInt32(uint value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (Int64)")] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 2)] + [InlineData(100, 3)] + [InlineData(1000, 4)] + [InlineData(10000, 5)] + [InlineData(100000, 6)] + [InlineData(1000000, 7)] + [InlineData(10000000, 8)] + [InlineData(100000000, 9)] + [InlineData(1000000000, 10)] + [InlineData(10000000000, 11)] + [InlineData(100000000000, 12)] + [InlineData(1000000000000, 13)] + [InlineData(10000000000000, 14)] + [InlineData(100000000000000, 15)] + [InlineData(1000000000000000, 16)] + [InlineData(10000000000000000, 17)] + [InlineData(100000000000000000, 18)] + [InlineData(1000000000000000000, 19)] + [InlineData(-1, 1)] + [InlineData(-10, 2)] + [InlineData(-100, 3)] + [InlineData(-1000, 4)] + [InlineData(-10000, 5)] + [InlineData(-100000, 6)] + [InlineData(-1000000, 7)] + [InlineData(-10000000, 8)] + [InlineData(-100000000, 9)] + [InlineData(-1000000000, 10)] + [InlineData(-10000000000, 11)] + [InlineData(-100000000000, 12)] + [InlineData(-1000000000000, 13)] + [InlineData(-10000000000000, 14)] + [InlineData(-100000000000000, 15)] + [InlineData(-1000000000000000, 16)] + [InlineData(-10000000000000000, 17)] + [InlineData(-100000000000000000, 18)] + [InlineData(-1000000000000000000, 19)] + [InlineData(long.MinValue, 19)] + [InlineData(long.MaxValue, 19)] + public void GenericMathIntegerLengthShouldProduceExpectedResultInt64(long value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (UInt64)")] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(10, 2)] + [InlineData(100, 3)] + [InlineData(1000, 4)] + [InlineData(10000, 5)] + [InlineData(100000, 6)] + [InlineData(1000000, 7)] + [InlineData(10000000, 8)] + [InlineData(100000000, 9)] + [InlineData(1000000000, 10)] + [InlineData(10000000000, 11)] + [InlineData(100000000000, 12)] + [InlineData(1000000000000, 13)] + [InlineData(10000000000000, 14)] + [InlineData(100000000000000, 15)] + [InlineData(1000000000000000, 16)] + [InlineData(10000000000000000, 17)] + [InlineData(100000000000000000, 18)] + [InlineData(1000000000000000000, 19)] + [InlineData(10000000000000000000, 20)] + [InlineData(ulong.MinValue, 1)] + [InlineData(ulong.MaxValue, 20)] + public void GenericMathIntegerLengthShouldProduceExpectedResultUInt64(ulong value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "GenericMath.IntegerLength should produce the expected result (Double)")] + [InlineData(0, 1)] + [InlineData(1.1, 1)] + [InlineData(10.1, 2)] + [InlineData(100.1, 3)] + [InlineData(1000.1, 4)] + [InlineData(10000.1, 5)] + [InlineData(100000.1, 6)] + [InlineData(1000000.1, 7)] + [InlineData(10000000.1, 8)] + [InlineData(100000000.1, 9)] + [InlineData(1000000000.1, 10)] + [InlineData(10000000000.1, 11)] + [InlineData(100000000000.1, 12)] + [InlineData(1000000000000.1, 13)] + [InlineData(10000000000000.1, 14)] + [InlineData(100000000000000.1, 15)] + [InlineData(1000000000000000.1, 16)] + [InlineData(10000000000000000.1, 17)] + [InlineData(100000000000000000.1, 18)] + [InlineData(1000000000000000000.1, 19)] + [InlineData(10000000000000000000.1, 20)] + [InlineData(double.MinValue, 309)] + [InlineData(double.MaxValue, 309)] + public void GenericMathIntegerLengthShouldProduceExpectedResultDouble(double value, int expected) + { + // When + int actual = GenericMath.IntegerLength(value); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/GenericMathMinMaxTests.cs b/OnixLabs.Numerics.UnitTests/GenericMathMinMaxTests.cs new file mode 100644 index 0000000..0fb0976 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/GenericMathMinMaxTests.cs @@ -0,0 +1,44 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class GenericMathMinMaxTests +{ + [Theory(DisplayName = "GenericMath.MinMax should produce the expected result")] + [InlineData(0, 0, 0, 0)] + [InlineData(0, 1, 0, 1)] + [InlineData(1, 0, 0, 1)] + [InlineData(-1, 0, -1, 0)] + [InlineData(0, -1, -1, 0)] + [InlineData(123, 456, 123, 456)] + [InlineData(456, 123, 123, 456)] + [InlineData(123.456, 456.789, 123.456, 456.789)] + [InlineData(456.789, 123.456, 123.456, 456.789)] + [InlineData(-123, 456, -123, 456)] + [InlineData(-456, 123, -456, 123)] + [InlineData(-123.456, 456.789, -123.456, 456.789)] + [InlineData(-456.789, 123.456, -456.789, 123.456)] + public void GenericMathMinMaxShouldProduceExpectedResult(double left, double right, double expectedMin, double expectedMax) + { + // When + (double actualMin, double actualMax) = GenericMath.MinMax(left, right); + + // Then + Assert.Equal(expectedMin, actualMin); + Assert.Equal(expectedMax, actualMax); + } +} diff --git a/OnixLabs.Numerics.UnitTests/NumberInfoConstantTests.cs b/OnixLabs.Numerics.UnitTests/NumberInfoConstantTests.cs new file mode 100644 index 0000000..2bfd308 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/NumberInfoConstantTests.cs @@ -0,0 +1,90 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class NumberInfoConstantTests +{ + [Fact(DisplayName = "NumberInfo.Zero should produce the expected result")] + public void NumberInfoZeroShouldProduceExpectedResult() + { + // Given + const int exponent = 0; + const int precision = 1; + const int sign = 0; + const int scale = 0; + BigInteger significand = 0; + BigInteger unscaledValue = 0; + + // When + NumberInfo candidate = NumberInfo.Zero; + + // Then + Assert.Equal(significand, candidate.Significand); + Assert.Equal(exponent, candidate.Exponent); + Assert.Equal(precision, candidate.Precision); + Assert.Equal(sign, candidate.Sign); + Assert.Equal(unscaledValue, candidate.UnscaledValue); + Assert.Equal(scale, candidate.Scale); + } + + [Fact(DisplayName = "NumberInfo.One should produce the expected result")] + public void NumberInfoOneShouldProduceExpectedResult() + { + // Given + const int exponent = 0; + const int precision = 1; + const int sign = 1; + const int scale = 0; + BigInteger significand = 1; + BigInteger unscaledValue = 1; + + // When + NumberInfo candidate = NumberInfo.One; + + // Then + Assert.Equal(significand, candidate.Significand); + Assert.Equal(exponent, candidate.Exponent); + Assert.Equal(precision, candidate.Precision); + Assert.Equal(sign, candidate.Sign); + Assert.Equal(unscaledValue, candidate.UnscaledValue); + Assert.Equal(scale, candidate.Scale); + } + + [Fact(DisplayName = "NumberInfo.NegativeOne should produce the expected result")] + public void NumberInfoNegativeOneShouldProduceExpectedResult() + { + // Given + const int exponent = 0; + const int precision = 1; + const int sign = -1; + const int scale = 0; + BigInteger significand = -1; + BigInteger unscaledValue = -1; + + // When + NumberInfo candidate = NumberInfo.NegativeOne; + + // Then + Assert.Equal(significand, candidate.Significand); + Assert.Equal(exponent, candidate.Exponent); + Assert.Equal(precision, candidate.Precision); + Assert.Equal(sign, candidate.Sign); + Assert.Equal(unscaledValue, candidate.UnscaledValue); + Assert.Equal(scale, candidate.Scale); + } +} diff --git a/OnixLabs.Numerics.UnitTests/NumberInfoEqualityComparerTests.cs b/OnixLabs.Numerics.UnitTests/NumberInfoEqualityComparerTests.cs new file mode 100644 index 0000000..f0613e5 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/NumberInfoEqualityComparerTests.cs @@ -0,0 +1,57 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class NumberInfoEqualityComparerTests +{ + [NumberInfoEqualityComparerData] + [Theory(DisplayName = "NumberInfoEqualityComparer.Equals should produce the expected result (Strict)")] + public void NumberInfoEqualityComparerEqualsShouldProduceExpectedResultStrict(decimal left, decimal right, Guid _) + { + // Given + bool expected = left == right && left.Scale == right.Scale; + NumberInfo leftCandidate = left.ToNumberInfo(); + NumberInfo rightCandidate = right.ToNumberInfo(); + + // When + bool actualFromComparer = NumberInfoEqualityComparer.Strict.Equals(leftCandidate, rightCandidate); + bool actualFromNumberInfo = leftCandidate.Equals(rightCandidate); + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromNumberInfo); + } + + [NumberInfoEqualityComparerData] + [Theory(DisplayName = "NumberInfoEqualityComparer.Equals should produce the expected result (Semantic)")] + public void NumberInfoEqualityComparerEqualsShouldProduceExpectedResultSemantic(decimal left, decimal right, Guid _) + { + // Given + bool expected = left == right; + NumberInfo leftCandidate = left.ToNumberInfo(); + NumberInfo rightCandidate = right.ToNumberInfo(); + + // When + bool actualFromComparer = NumberInfoEqualityComparer.Semantic.Equals(leftCandidate, rightCandidate); + bool actualFromNumberInfo = leftCandidate == rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromNumberInfo); + } +} diff --git a/OnixLabs.Numerics.UnitTests/NumberInfoOrdinalityComparerTests.cs b/OnixLabs.Numerics.UnitTests/NumberInfoOrdinalityComparerTests.cs new file mode 100644 index 0000000..2138fcd --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/NumberInfoOrdinalityComparerTests.cs @@ -0,0 +1,129 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class NumberInfoOrdinalityComparerTests +{ + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "NumberInfoOrdinalityComparer.Compare should produce the expected result")] + public void NumberInfoOrdinalityComparerCompareShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + int expected = left.CompareTo(right); + NumberInfo leftCandidate = left.ToNumberInfo(); + NumberInfo rightCandidate = right.ToNumberInfo(); + + // When + int actualFromComparer = NumberInfoOrdinalityComparer.Default.Compare(leftCandidate, rightCandidate); + int actualFromNumberInfo = leftCandidate.CompareTo(rightCandidate); + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromNumberInfo); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "NumberInfoOrdinalityComparer.IsEqual should produce the expected result")] + public void NumberInfoOrdinalityComparerIsEqualShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left.CompareTo(right) is 0; + NumberInfo leftCandidate = left.ToNumberInfo(); + NumberInfo rightCandidate = right.ToNumberInfo(); + + // When + bool actualFromComparer = NumberInfoOrdinalityComparer.Default.IsEqual(leftCandidate, rightCandidate); + bool actualFromNumberInfo = leftCandidate.CompareTo(rightCandidate) is 0; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromNumberInfo); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "NumberInfoOrdinalityComparer.IsGreaterThan should produce the expected result")] + public void NumberInfoOrdinalityComparerIsGreaterThanShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left > right; + NumberInfo leftCandidate = left.ToNumberInfo(); + NumberInfo rightCandidate = right.ToNumberInfo(); + + // When + bool actualFromComparer = NumberInfoOrdinalityComparer.Default.IsGreaterThan(leftCandidate, rightCandidate); + bool actualFromNumberInfo = leftCandidate > rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromNumberInfo); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "NumberInfoOrdinalityComparer.IsGreaterThanOrEqual should produce the expected result")] + public void NumberInfoOrdinalityComparerIsGreaterOrEqualThanShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left >= right; + NumberInfo leftCandidate = left.ToNumberInfo(); + NumberInfo rightCandidate = right.ToNumberInfo(); + + // When + bool actualFromComparer = NumberInfoOrdinalityComparer.Default.IsGreaterThanOrEqual(leftCandidate, rightCandidate); + bool actualFromNumberInfo = leftCandidate >= rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromNumberInfo); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "NumberInfoOrdinalityComparer.IsLessThan should produce the expected result")] + public void NumberInfoOrdinalityComparerIsLessThanShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left < right; + NumberInfo leftCandidate = left.ToNumberInfo(); + NumberInfo rightCandidate = right.ToNumberInfo(); + + // When + bool actualFromComparer = NumberInfoOrdinalityComparer.Default.IsLessThan(leftCandidate, rightCandidate); + bool actualFromNumberInfo = leftCandidate < rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromNumberInfo); + } + + [NumberInfoOrdinalityComparerData] + [Theory(DisplayName = "NumberInfoOrdinalityComparer.IsLessThanOrEqual should produce the expected result")] + public void NumberInfoOrdinalityComparerIsLessOrEqualThanShouldProduceExpectedResult(decimal left, decimal right, Guid _) + { + // Given + bool expected = left <= right; + NumberInfo leftCandidate = left.ToNumberInfo(); + NumberInfo rightCandidate = right.ToNumberInfo(); + + // When + bool actualFromComparer = NumberInfoOrdinalityComparer.Default.IsLessThanOrEqual(leftCandidate, rightCandidate); + bool actualFromNumberInfo = leftCandidate <= rightCandidate; + + // Then + Assert.Equal(expected, actualFromComparer); + Assert.Equal(expected, actualFromNumberInfo); + } +} diff --git a/OnixLabs.Numerics.UnitTests/NumberInfoParseTests.cs b/OnixLabs.Numerics.UnitTests/NumberInfoParseTests.cs new file mode 100644 index 0000000..3861b3c --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/NumberInfoParseTests.cs @@ -0,0 +1,112 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class NumberInfoParseTests +{ + [NumberFormatData] + [Theory(DisplayName = "NumberInfo.Parse should produce the expected result (Currency)")] + public void NumberInfoParseShouldProduceExpectedResultCurrency(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToString("C", culture); + NumberInfo expected = decimal.Parse(formatted, NumberStyles.Currency, culture).ToNumberInfo(); + + // When + NumberInfo actual = NumberInfo.Parse(formatted, NumberStyles.Currency, culture); + + // Then + Assert.Equal(expected, actual, NumberInfoEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "NumberInfo.Parse should produce the expected result (Decimal, Integer)")] + public void NumberInfoParseShouldProduceExpectedResultDecimalInteger(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = Int128.CreateTruncating(value).ToString("D", culture); + NumberInfo expected = Int128.Parse(formatted, NumberStyles.Integer, culture).ToNumberInfo(); + + // When + NumberInfo actual = NumberInfo.Parse(formatted, NumberStyles.Integer, culture); + + // Then + Assert.Equal(expected, actual, NumberInfoEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "NumberInfo.Parse should produce the expected result (Decimal, BigDecimal)")] + public void NumberInfoParseShouldProduceExpectedResultDecimalBigDecimal(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToBigDecimal().ToString("D", culture); + NumberInfo expected = value.ToNumberInfo(); + + // When + NumberInfo actual = NumberInfo.Parse(formatted, NumberStyles.Number, culture); + + // Then + Assert.Equal(expected, actual, NumberInfoEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "NumberInfo.Parse should produce the expected result (Fixed)")] + public void NumberInfoParseShouldProduceExpectedResultFixed(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToString("F", culture); + NumberInfo expected = decimal.Parse(formatted, NumberStyles.Float, culture).ToNumberInfo(); + + // When + NumberInfo actual = NumberInfo.Parse(formatted, NumberStyles.Float, culture); + + // Then + Assert.Equal(expected, actual, NumberInfoEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "NumberInfo.Parse should produce the expected result (General)")] + public void NumberInfoParseShouldProduceExpectedResultGeneral(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToString("G", culture); + NumberInfo expected = decimal.Parse(formatted, NumberStyles.Any, culture).ToNumberInfo(); + + // When + NumberInfo actual = NumberInfo.Parse(formatted, NumberStyles.Any, culture); + + // Then + Assert.Equal(expected, actual, NumberInfoEqualityComparer.Semantic); + } + + [NumberFormatData] + [Theory(DisplayName = "NumberInfo.Parse should produce the expected result (Number)")] + public void NumberInfoParseShouldProduceExpectedResultNumber(decimal value, CultureInfo culture, Guid _) + { + // Given + string formatted = value.ToString("N", culture); + NumberInfo expected = decimal.Parse(formatted, NumberStyles.Number, culture).ToNumberInfo(); + + // When + NumberInfo actual = NumberInfo.Parse(formatted, NumberStyles.Number, culture); + + // Then + Assert.Equal(expected, actual, NumberInfoEqualityComparer.Semantic); + } +} diff --git a/OnixLabs.Numerics.UnitTests/NumberInfoToStringTests.cs b/OnixLabs.Numerics.UnitTests/NumberInfoToStringTests.cs new file mode 100644 index 0000000..10814e0 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/NumberInfoToStringTests.cs @@ -0,0 +1,37 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class NumberInfoToStringTests +{ + [NumberFormatData] + [Theory(DisplayName = "NumberInfo.ToString should produce the expected result (General)")] + public void NumberInfoToStringShouldProduceExpectedResultGeneral(decimal value, CultureInfo culture, Guid _) + { + // Given + string expected = value.ToString("G", culture); + NumberInfo candidate = value.ToNumberInfo(); + + // When + string actual = candidate.ToString("G", culture); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Numerics.UnitTests/NumericsExtensionsTests.cs b/OnixLabs.Numerics.UnitTests/NumericsExtensionsTests.cs new file mode 100644 index 0000000..0bf7256 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/NumericsExtensionsTests.cs @@ -0,0 +1,230 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using System.Numerics; +using OnixLabs.Numerics.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Numerics.UnitTests; + +public sealed class NumericExtensionsTests +{ + [Theory(DisplayName = "Decimal.GetUnscaledValue should produce the expected result (decimal values)")] + [InlineData(0, 0)] + [InlineData(1, 1)] + [InlineData(-1, -1)] + [InlineData(0.000000001, 1)] + [InlineData(-0.000000001, -1)] + [InlineData(123456789, 123456789)] + [InlineData(12345678.9, 123456789)] + [InlineData(1234567.89, 123456789)] + [InlineData(123456.789, 123456789)] + [InlineData(12345.6789, 123456789)] + [InlineData(1234.56789, 123456789)] + [InlineData(123.456789, 123456789)] + [InlineData(12.3456789, 123456789)] + [InlineData(1.23456789, 123456789)] + [InlineData(-123456789, -123456789)] + [InlineData(-12345678.9, -123456789)] + [InlineData(-1234567.89, -123456789)] + [InlineData(-123456.789, -123456789)] + [InlineData(-12345.6789, -123456789)] + [InlineData(-1234.56789, -123456789)] + [InlineData(-123.456789, -123456789)] + [InlineData(-12.3456789, -123456789)] + [InlineData(-1.23456789, -123456789)] + [InlineData(1.000000001, 1000000001)] + public void DecimalGetUnscaledValueShouldProduceExpectedResultDecimal(decimal value, BigInteger expected) + { + // When + BigInteger actual = value.GetUnscaledValue(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Decimal.GetUnscaledValue should produce the expected result (value and scale)")] + [InlineData(123, 0, 123)] + [InlineData(123, 1, 1230)] + [InlineData(123, 2, 12300)] + [InlineData(123, 3, 123000)] + [InlineData(123, 4, 1230000)] + [InlineData(123, 5, 12300000)] + [InlineData(123, 6, 123000000)] + [InlineData(123, 7, 1230000000)] + [InlineData(123, 8, 12300000000)] + [InlineData(123, 9, 123000000000)] + [InlineData(123, 10, 1230000000000)] + public void DecimalGetUnscaledValueShouldProduceExpectedResultValueAndScale(int value, int scale, BigInteger expected) + { + // Given + decimal candidate = value.ToDecimal(scale, ScaleMode.Integral); + + // When + BigInteger actual = candidate.GetUnscaledValue(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "INumber.IsBetween should produce the expected result")] + [InlineData(0, 0, 0, true)] + [InlineData(0, 0, 1, true)] + [InlineData(0, -1, 1, true)] + [InlineData(0, 1, 2, false)] + public void NumberIsBetweenShouldProduceExpectedResult(int value, int minimum, int maximum, bool expected) + { + // When + bool actual = value.IsBetween(minimum, maximum); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "BinaryInteger.ToBigInteger should produce the expected result")] + [InlineData(0, 0)] + [InlineData(1, 1)] + [InlineData(-1, -1)] + [InlineData(123456789, 123456789)] + [InlineData(-123456789, -123456789)] + public void BinaryIntegerToBigIntegerShouldProduceExpectedResult(int value, BigInteger expected) + { + // When + BigInteger actual = value.ToBigInteger(); + + // Then + Assert.Equal(expected, actual); + } + + [NumericsExtensionsToDecimalData] + [Theory(DisplayName = "IBinaryInteger.ToDecimal should produce the expected result")] + public void BinaryIntegerToDecimalShouldProduceExpectedResult(Int128 value, int scale, ScaleMode mode, string expected) + { + // When + decimal candidate = value.ToDecimal(scale, mode); + string actual = candidate.ToString(CultureInfo.InvariantCulture); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "IBinaryInteger.ToDecimal should throw InvalidOperationException if a value is too large.")] + public void BinaryIntegerToDecimalShouldThrowInvalidOperationExceptionIfAValueIsTooLarge() + { + // When + InvalidOperationException exception = Assert.Throws(() => Int128.MaxValue.ToDecimal()); + + // Then + Assert.Equal($"Value is either too large or too small to convert to {nameof(Decimal)}.", exception.Message); + } + + [Fact(DisplayName = "IBinaryInteger.ToDecimal should throw InvalidOperationException if a value is too small.")] + public void BinaryIntegerToDecimalShouldThrowInvalidOperationExceptionIfAValueIsTooSmall() + { + // When + InvalidOperationException exception = Assert.Throws(() => Int128.MinValue.ToDecimal()); + + // Then + Assert.Equal($"Value is either too large or too small to convert to {nameof(Decimal)}.", exception.Message); + } + + [Fact(DisplayName = "IBinaryInteger.ToDecimal should throw ArgumentException if scale is less than zero.")] + public void BinaryIntegerToDecimalShouldThrowArgumentExceptionIfScaleIsLessThanZero() + { + // When + ArgumentException exception = Assert.Throws(() => 0.ToDecimal(-1)); + + // Then + Assert.Equal("Scale must be between 0 and 28.", exception.Message); + } + + [Fact(DisplayName = "IBinaryInteger.ToDecimal should throw ArgumentException if mode is not defined.")] + public void BinaryIntegerToDecimalShouldThrowArgumentExceptionIfModeIsNotDefined() + { + // When + ArgumentOutOfRangeException exception = Assert.Throws(() => 0.ToDecimal(0, (ScaleMode)2)); + + // Then + Assert.Equal("Invalid ScaleMode enum value: 2. Valid values include: Fractional, Integral. (Parameter 'mode')", exception.Message); + } + + [NumericsExtensionsToNumberInfoIntegerData] + [Theory(DisplayName = "IBinaryInteger.ToNumberInfo should produce the expected result")] + public void BinaryIntegerToNumberInfoProduceExpectedResultIntegerValues + (Int128 value, BigInteger unscaledValue, int scale, BigInteger significand, int exponent, int sign, int precision) + { + // When + NumberInfo candidate = value.ToNumberInfo(); + + // Then + Assert.Equal(significand, candidate.Significand); + Assert.Equal(exponent, candidate.Exponent); + Assert.Equal(precision, candidate.Precision); + Assert.Equal(sign, candidate.Sign); + Assert.Equal(unscaledValue, candidate.UnscaledValue); + Assert.Equal(scale, candidate.Scale); + } + + [NumericsExtensionsToNumberInfoFloatData] + [Theory(DisplayName = "Single.ToNumberInfo should produce the expected result")] + public void SingleToNumberInfoShouldProduceExpectedResultFloatValues + (float value, BigInteger unscaledValue, int scale, BigInteger significand, int exponent, int sign, int precision) + { + // When + NumberInfo candidate = value.ToNumberInfo(); + + // Then + Assert.Equal(significand, candidate.Significand); + Assert.Equal(exponent, candidate.Exponent); + Assert.Equal(precision, candidate.Precision); + Assert.Equal(sign, candidate.Sign); + Assert.Equal(unscaledValue, candidate.UnscaledValue); + Assert.Equal(scale, candidate.Scale); + } + + [NumericsExtensionsToNumberInfoDoubleData] + [Theory(DisplayName = "Double.ToNumberInfo should produce the expected result")] + public void NumberInfoCreateShouldProduceExpectedResultDoubleValues + (double value, BigInteger unscaledValue, int scale, BigInteger significand, int exponent, int sign, int precision) + { + // When + NumberInfo candidate = value.ToNumberInfo(); + + // Then + Assert.Equal(significand, candidate.Significand); + Assert.Equal(exponent, candidate.Exponent); + Assert.Equal(precision, candidate.Precision); + Assert.Equal(sign, candidate.Sign); + Assert.Equal(unscaledValue, candidate.UnscaledValue); + Assert.Equal(scale, candidate.Scale); + } + + [NumericsExtensionsToNumberInfoDecimalData] + [Theory(DisplayName = "Decimal.ToNumberInfo should produce the expected result")] + public void NumberInfoCreateShouldProduceExpectedResultDecimalValues + (decimal value, BigInteger unscaledValue, int scale, BigInteger significand, int exponent, int sign, int precision) + { + // When + NumberInfo candidate = value.ToNumberInfo(); + + // Then + Assert.Equal(significand, candidate.Significand); + Assert.Equal(exponent, candidate.Exponent); + Assert.Equal(precision, candidate.Precision); + Assert.Equal(sign, candidate.Sign); + Assert.Equal(unscaledValue, candidate.UnscaledValue); + Assert.Equal(scale, candidate.Scale); + } +} diff --git a/OnixLabs.Numerics.UnitTests/OnixLabs.Numerics.UnitTests.csproj b/OnixLabs.Numerics.UnitTests/OnixLabs.Numerics.UnitTests.csproj new file mode 100644 index 0000000..dfe9b76 --- /dev/null +++ b/OnixLabs.Numerics.UnitTests/OnixLabs.Numerics.UnitTests.csproj @@ -0,0 +1,29 @@ + + + net8.0 + enable + enable + + false + true + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Abs.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Abs.cs new file mode 100644 index 0000000..a591696 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Abs.cs @@ -0,0 +1,30 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Gets the absolute value of the specified value. + /// + /// The from which to obtain an absolute value. + /// Returns the absolute value of the specified value. + public static BigDecimal Abs(BigDecimal value) + { + return new BigDecimal(BigInteger.Abs(value.UnscaledValue), value.Scale); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Addition.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Addition.cs new file mode 100644 index 0000000..57c1159 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Addition.cs @@ -0,0 +1,50 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Computes the sum of the specified values. + /// + /// The left-hand value to add to. + /// The right-hand value to add. + /// Returns the sum of the specified values. + public static BigDecimal Add(BigDecimal left, BigDecimal right) + { + int scale = MaxScale(left, right); + + if (IsZero(left)) return right.SetScale(scale); + if (IsZero(right)) return left.SetScale(scale); + + (BigInteger leftAddend, BigInteger rightAddend) = NormalizeUnscaledValues(left, right); + BigInteger sum = leftAddend + rightAddend; + + return new BigDecimal(sum, scale); + } + + /// + /// Computes the sum of the specified values. + /// + /// The left-hand value to add to. + /// The right-hand value to add. + /// Returns the sum of the specified values. + public static BigDecimal operator +(BigDecimal left, BigDecimal right) + { + return Add(left, right); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Decrement.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Decrement.cs new file mode 100644 index 0000000..c0d9592 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Decrement.cs @@ -0,0 +1,50 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Decrements the integral component of the specified value by one. + /// + /// The value to decrement. + /// Returns a new value decremented by one integral unit. + public static BigDecimal Decrement(BigDecimal value) + { + return new BigDecimal(value.UnscaledValue - value.number.ScaleFactor, value.Scale); + } + + /// + /// Decrements the fractional component of the specified value by one. + /// + /// The value to decrement. + /// Returns a new value decremented by one fractional unit. + public static BigDecimal DecrementFraction(BigDecimal value) + { + return new BigDecimal(value.UnscaledValue - BigInteger.One, value.Scale); + } + + /// + /// Decrements the integral component of the specified value by one. + /// + /// The value to decrement. + /// Returns a new value decremented by one integral unit. + public static BigDecimal operator --(BigDecimal value) + { + return Decrement(value); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Division.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Division.cs new file mode 100644 index 0000000..7601563 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Division.cs @@ -0,0 +1,80 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Computes the quotient of the specified values. + /// + /// The left-hand value to divide. + /// The right-hand value to divide by. + /// The rounding strategy to use. The default value is . + /// Returns the quotient of the specified values. + public static BigDecimal Divide(BigDecimal left, BigDecimal right, MidpointRounding mode = default) + { + RequireIsDefined(mode, nameof(mode)); + + if (right == Zero) throw new DivideByZeroException(); + if (right == One) return left; + if (left == Zero) return Zero; + + BigInteger dividend = left.UnscaledValue * BigInteger.Pow(10, right.Scale); + BigInteger quotient = DivideAndRound(dividend, right.UnscaledValue, mode); + + return new BigDecimal(quotient, left.Scale); + } + + /// + /// Computes the quotient of the specified values. + /// + /// The left-hand value to divide. + /// The right-hand value to divide by. + /// Returns the quotient of the specified values. + public static BigDecimal operator /(BigDecimal left, BigDecimal right) + { + return Divide(left, right); + } + + /// + /// Divides the specified dividend by the specified divisor and rounds the remainder using the specified rounding mode. + /// + /// The dividend value to divide. + /// The divisor to divide by. + /// The rounding strategy to use. The default value is . + /// Returns the quotient of the specified values, rounded using the specified rounding mode. + private static BigInteger DivideAndRound(BigInteger dividend, BigInteger divisor, MidpointRounding mode) + { + // 1. Obtain the quotient and remainder by dividing the dividend by the divisor. + (BigInteger quotient, BigInteger remainder) = BigInteger.DivRem(dividend, divisor); + + // 2. Obtain the unit value of the quotient as this is required to accurately round towards an even number. + int unit = (int)(quotient % 10); + + // 3. Obtain the remainder with 10 digits of precision, which is required for accurate rounding. + remainder = remainder * RoundingMagnitude / divisor; + + // 4. Convert the remainder to a double with 10 digits of fractional precision and add the unit. + double valueToRound = (double)remainder / RoundingMagnitude + unit; + + // 5. Round until zero fractional digits remain and subtract the unit; yields 1 or 0. + int rounded = (int)Math.Round(valueToRound, mode) - unit; + + return quotient + rounded; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Increment.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Increment.cs new file mode 100644 index 0000000..e07be52 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Increment.cs @@ -0,0 +1,53 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Increments the integral component of the specified value by one. + /// + /// The value to increment. + /// Returns a new value incremented by one integral unit. + public static BigDecimal Increment(BigDecimal value) + { + BigInteger power = BigInteger.Pow(10, value.Scale); + (BigInteger quotient, BigInteger remainder) = BigInteger.DivRem(value.UnscaledValue, power); + + return new BigDecimal((quotient + 1) * power + remainder, value.Scale); + } + + /// + /// Increments the fractional component of the specified value by one. + /// + /// The value to increment. + /// Returns a new value incremented by one fractional unit. + public static BigDecimal IncrementFraction(BigDecimal value) + { + return new BigDecimal(value.UnscaledValue + 1, value.Scale); + } + + /// + /// Increments the integral component of the specified value by one. + /// + /// The value to increment. + /// Returns a new value incremented by one integral unit. + public static BigDecimal operator ++(BigDecimal value) + { + return Increment(value); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Modulus.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Modulus.cs new file mode 100644 index 0000000..35d0d0b --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Modulus.cs @@ -0,0 +1,47 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Gets the modulus of the specified values by dividing the left-hand value by the right-hand value. + /// + /// The left-hand value to divide. + /// The right-hand value to divide by. + /// Returns the modulus of the specified values by dividing the left-hand value by the right-hand value. + public static BigDecimal Mod(BigDecimal left, BigDecimal right) + { + int scale = MaxScale(left, right); + + (BigInteger dividend, BigInteger divisor) = NormalizeUnscaledValues(left, right); + BigInteger remainder = BigInteger.Remainder(dividend, divisor); + + return new BigDecimal(remainder, scale); + } + + /// + /// Gets the modulus of the specified values by dividing the left-hand value by the right-hand value. + /// + /// The left-hand value to divide. + /// The right-hand value to divide by. + /// Returns the modulus of the specified values by dividing the left-hand value by the right-hand value. + public static BigDecimal operator %(BigDecimal left, BigDecimal right) + { + return Mod(left, right); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Multiplication.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Multiplication.cs new file mode 100644 index 0000000..4055dae --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Multiplication.cs @@ -0,0 +1,47 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Computes the product of the specified values. + /// + /// The left-hand value to multiply by. + /// The right-hand value to multiply. + /// Returns the product of the specified values. + public static BigDecimal Multiply(BigDecimal left, BigDecimal right) + { + int scale = left.Scale + right.Scale; + + if (IsZero(left) || IsZero(right)) return new BigDecimal(0, scale); + BigInteger product = left.UnscaledValue * right.UnscaledValue; + + return new BigDecimal(product, scale); + } + + /// + /// Computes the product of the specified values. + /// + /// The left-hand value to multiply by. + /// The right-hand value to multiply. + /// Returns the product of the specified values. + public static BigDecimal operator *(BigDecimal left, BigDecimal right) + { + return Multiply(left, right); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Pow.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Pow.cs new file mode 100644 index 0000000..c9addb3 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Pow.cs @@ -0,0 +1,45 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Computes the power of the specified value, raised to the power of the specified exponent. + /// + /// The value to raise. + /// The exponent to raise by. + /// Specifies the rounding mode that should be applied for values raised to the power of a negative exponent. + /// Returns the power of the specified value, raised to the power of the specified exponent. + public static BigDecimal Pow(BigDecimal value, int exponent, MidpointRounding mode = default) + { + if (IsZero(value)) + { + if (exponent < 0) throw new DivideByZeroException(); + return exponent is 0 ? One : Zero; + } + + if (exponent is 0) return One; + if (exponent is 1) return value; + + BigDecimal result = Abs(value); + int absExponent = int.Abs(exponent); + while (--absExponent > 0) result *= Abs(value); + + return (exponent < 0 ? Divide(One.SetScale(value.Scale), result, mode) : result) * value.number.Sign; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Round.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Round.cs new file mode 100644 index 0000000..99de06b --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Round.cs @@ -0,0 +1,98 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Rounds the specified value. + /// + /// The value to round. + /// The number of decimal places to round by. The default value is zero. + /// The rounding mode to round by. The default value is . + /// Returns a value rounded to the specified number of fractional digits. + /// If the specified scale is non-negative. + /// If the specified rounding mode is invalid. + public static BigDecimal Round(BigDecimal value, int scale = default, MidpointRounding mode = default) + { + Require(scale >= 0, "Scale must be greater than or equal to zero.", nameof(scale)); + RequireIsDefined(mode, nameof(mode)); + + if (scale >= value.Scale) return value; + + BigInteger divisor = BigInteger.Pow(10, value.Scale - scale); + BigInteger quotient = DivideAndRound(value.UnscaledValue, divisor, mode); + + return new BigDecimal(quotient, scale); + } + + /// + /// Rounds the specified value up to the smallest integral value greater than or equal to the specified number. + /// This kind of rounding is sometimes called rounding towards positive infinity, following IEEE Standard 754, section 4. + /// + /// The value to be rounded up towards positive infinity. + /// Returns the smallest integral value that is greater than or equal to the specified value. + public static BigDecimal Ceiling(BigDecimal value) + { + return Round(value, 0, MidpointRounding.ToPositiveInfinity); + } + + /// + /// Rounds the specified value down to the largest integral value less than or equal to the specified number. + /// This kind of rounding is sometimes called rounding towards negative infinity, following IEEE Standard 754, section 4. + /// + /// The value to be rounded down towards positive infinity. + /// Returns the largest integral value that is less than or equal to the specified value. + public static BigDecimal Floor(BigDecimal value) + { + return Round(value, 0, MidpointRounding.ToNegativeInfinity); + } + + /// + /// Rounds the current value. + /// + /// The number of decimal places to round by. The default value is zero. + /// The rounding mode to round by. The default value is . + /// Returns a value rounded to the specified number of fractional digits. + /// If the specified scale is non-negative. + /// If the specified rounding mode is invalid. + public BigDecimal Round(int scale = default, MidpointRounding mode = default) + { + return Round(this, scale, mode); + } + + /// + /// Rounds the current value up to the smallest integral value greater than or equal to the current number. + /// This kind of rounding is sometimes called rounding towards positive infinity, following IEEE Standard 754, section 4. + /// + /// Returns the smallest integral value that is greater than or equal to the current value. + public BigDecimal Ceiling() + { + return Ceiling(this); + } + + /// + /// Rounds the current value down to the largest integral value less than or equal to the current number. + /// This kind of rounding is sometimes called rounding towards negative infinity, following IEEE Standard 754, section 4. + /// + /// Returns the largest integral value that is less than or equal to the current value. + public BigDecimal Floor() + { + return Floor(this); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Scale.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Scale.cs new file mode 100644 index 0000000..890e447 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Scale.cs @@ -0,0 +1,66 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Sets the scale of the specified value. + /// + /// The value to scale. + /// The scale to apply to the value. + /// The mode to be used when the specified scale is less than the current scale. + /// Returns a new value with the specified scale. + public static BigDecimal SetScale(BigDecimal value, int scale, MidpointRounding mode = default) + { + Require(scale >= 0, "Scale must be greater than or equal to zero.", nameof(scale)); + RequireIsDefined(mode, nameof(mode)); + + if (scale == value.Scale) return value; + if (scale < value.Scale) return Round(value, scale, mode); + + BigInteger magnitude = BigInteger.Pow(10, scale - value.Scale); + return new BigDecimal(value.UnscaledValue * magnitude, scale); + } + + /// + /// Sets the scale of the specified value. + /// + /// The scale to apply to the value. + /// The mode to be used when the specified scale is less than the current scale. + /// Returns a new value with the specified scale. + public BigDecimal SetScale(int scale, MidpointRounding mode = default) + { + return SetScale(this, scale, mode); + } + + /// + /// Normalizes the unscaled values of the specified values. + /// + /// The left-hand value to normalize. + /// The right-hand value to normalize. + /// Returns the normalized unscaled values of the specified values. + public static (BigInteger Left, BigInteger Right) NormalizeUnscaledValues(BigDecimal left, BigDecimal right) + { + BigInteger minOrderOfMagnitude = BigInteger.Min(left.number.ScaleFactor, right.number.ScaleFactor); + BigInteger leftNormalized = left.UnscaledValue * right.number.ScaleFactor / minOrderOfMagnitude; + BigInteger rightNormalized = right.UnscaledValue * left.number.ScaleFactor / minOrderOfMagnitude; + + return (leftNormalized, rightNormalized); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Subtraction.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Subtraction.cs new file mode 100644 index 0000000..248ddab --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Subtraction.cs @@ -0,0 +1,50 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Computes the difference between the specified values. + /// + /// The left-hand value to subtract from. + /// The right-hand value to subtract. + /// Returns the difference between the specified values. + public static BigDecimal Subtract(BigDecimal left, BigDecimal right) + { + int scale = MaxScale(left, right); + + if (IsZero(left)) return -right.SetScale(scale); + if (IsZero(right)) return left.SetScale(scale); + + (BigInteger minuend, BigInteger subtrahend) = NormalizeUnscaledValues(left, right); + BigInteger difference = minuend - subtrahend; + + return new BigDecimal(difference, scale); + } + + /// + /// Computes the difference between the specified values. + /// + /// The left-hand value to subtract from. + /// The right-hand value to subtract. + /// Returns the difference between the specified values. + public static BigDecimal operator -(BigDecimal left, BigDecimal right) + { + return Subtract(left, right); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Trim.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Trim.cs new file mode 100644 index 0000000..dd8b681 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Trim.cs @@ -0,0 +1,43 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Trims any trailing zeros from the fractional part of the specified value. + /// + /// The value from which to trim trailing zeros. + /// Returns a new excluding any trailing zeros. + public static BigDecimal TrimTrailingZeros(BigDecimal value) + { + if (IsZero(value)) return Zero; + + int exponent = 0; + while (value.UnscaledValue % BigInteger.Pow(10, exponent) == 0) exponent++; + return new BigDecimal(value.UnscaledValue / BigInteger.Pow(10, --exponent), value.Scale - exponent); + } + + /// + /// Trims any trailing zeros from the fractional part of the current value. + /// + /// Returns a new excluding any trailing zeros. + public BigDecimal TrimTrailingZeros() + { + return TrimTrailingZeros(this); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.Truncate.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.Truncate.cs new file mode 100644 index 0000000..1b2bb23 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.Truncate.cs @@ -0,0 +1,27 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Truncates the fractional part of the specified value, leaving only the integral component. + /// + /// Returns the fractional part of the specified value, leaving only the integral component. + public static BigDecimal Truncate(BigDecimal value) + { + return value.number.Integer; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.UnaryAddition.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.UnaryAddition.cs new file mode 100644 index 0000000..aa96e0d --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.UnaryAddition.cs @@ -0,0 +1,38 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Computes the unary addition of the specified value. + /// + /// The value for which to perform unary addition. + /// Returns the unary addition of the specified value. + public static BigDecimal UnaryAdd(BigDecimal value) + { + return value; + } + + /// + /// Computes the unary addition of the specified value. + /// + /// The value for which to perform unary addition. + /// Returns the unary addition of the specified value. + public static BigDecimal operator +(BigDecimal value) + { + return UnaryAdd(value); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Arithmetic.UnarySubtraction.cs b/OnixLabs.Numerics/BigDecimal.Arithmetic.UnarySubtraction.cs new file mode 100644 index 0000000..6f31f2c --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Arithmetic.UnarySubtraction.cs @@ -0,0 +1,38 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Computes the unary subtraction of the specified value. + /// + /// The value for which to perform unary subtraction. + /// Returns the unary subtraction of the specified value. + public static BigDecimal UnarySubtract(BigDecimal value) + { + return new BigDecimal(-value.UnscaledValue, value.Scale); + } + + /// + /// Computes the unary subtraction of the specified value. + /// + /// The value for which to perform unary subtraction. + /// Returns the unary subtraction of the specified value. + public static BigDecimal operator -(BigDecimal value) + { + return UnarySubtract(value); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Comparable.MinMax.cs b/OnixLabs.Numerics/BigDecimal.Comparable.MinMax.cs new file mode 100644 index 0000000..b9c9238 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Comparable.MinMax.cs @@ -0,0 +1,156 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Gets the lesser of the specified values. + /// + /// The left value to compare. + /// The right value to compare. + /// Returns the lesser of the specified values. + public static BigDecimal Min(BigDecimal left, BigDecimal right) + { + return left < right ? left : right; + } + + /// + /// Gets the greater of the specified values. + /// + /// The left value to compare. + /// The right value to compare. + /// Returns the greater of the specified values. + public static BigDecimal Max(BigDecimal left, BigDecimal right) + { + return left > right ? left : right; + } + + /// + /// Gets the lesser and the greater of the specified values. + /// + /// The left value to compare. + /// The right value to compare. + /// Returns the lesser and the greater of the specified values. + public static (BigDecimal Min, BigDecimal Max) MinMax(BigDecimal left, BigDecimal right) + { + BigDecimal min = Min(left, right); + BigDecimal max = Max(left, right); + + return (min, max); + } + + /// + /// Gets the lesser of the specified values. + /// + /// The left value to compare. + /// The right value to compare. + /// Returns the lesser of the specified values. + public static BigDecimal MinMagnitude(BigDecimal x, BigDecimal y) + { + BigDecimal left = Abs(x); + BigDecimal right = Abs(y); + + return left < right || left == right && IsNegative(x) ? x : y; + } + + /// + /// Gets the greater of the specified values. + /// + /// The left value to compare. + /// The right value to compare. + /// Returns the greater of the specified values. + public static BigDecimal MaxMagnitude(BigDecimal x, BigDecimal y) + { + BigDecimal left = Abs(x); + BigDecimal right = Abs(y); + + return left > right || left == right && !IsNegative(x) ? x : y; + } + + /// + /// Gets the lesser and the greater of the specified values by magnitude. + /// + /// The left value to compare. + /// The right value to compare. + /// Returns the lesser and the greater of the specified values by magnitude. + public static (BigDecimal MinMagnitude, BigDecimal MaxMagnitude) MinMaxMagnitude(BigDecimal left, BigDecimal right) + { + BigDecimal minMagnitude = MinMagnitude(left, right); + BigDecimal maxMagnitude = MaxMagnitude(left, right); + + return (minMagnitude, maxMagnitude); + } + + /// + /// Obtains the minimum scale of the specified left-hand and right-hand values. + /// + /// The left-hand value from which to obtain the minimum scale value. + /// The left-hand value from which to obtain the minimum scale value. + /// Returns the minimum scale of the specified left-hand and right-hand values. + public static int MinScale(BigDecimal left, BigDecimal right) + { + return int.Min(left.Scale, right.Scale); + } + + /// + /// Obtains the maximum scale of the specified left-hand and right-hand values. + /// + /// The left-hand value from which to obtain the maximum scale value. + /// The left-hand value from which to obtain the maximum scale value. + /// Returns the maximum scale of the specified left-hand and right-hand values. + public static int MaxScale(BigDecimal left, BigDecimal right) + { + return int.Max(left.Scale, right.Scale); + } + + /// + /// Obtains the minimum and maximum scale of the specified left-hand and right-hand values. + /// + /// The left-hand value from which to obtain the minimum and maximum values. + /// The left-hand value from which to obtain the minimum and maximum values. + /// Returns the minimum and maximum of the specified left-hand and right-hand values. + public static (int Min, int Max) MinMaxScale(BigDecimal left, BigDecimal right) + { + int min = MinScale(left, right); + int max = MaxScale(left, right); + + return (min, max); + } + + /// + /// Gets the lesser of the specified values. + /// + /// The left value to compare. + /// The right value to compare. + /// Returns the lesser of the specified values. + static BigDecimal INumberBase.MinMagnitudeNumber(BigDecimal x, BigDecimal y) + { + return MinMagnitude(x, y); + } + + /// + /// Gets the greater of the specified values. + /// + /// The left value to compare. + /// The right value to compare. + /// Returns the greater of the specified values. + static BigDecimal INumberBase.MaxMagnitudeNumber(BigDecimal x, BigDecimal y) + { + return MaxMagnitude(x, y); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Comparable.cs b/OnixLabs.Numerics/BigDecimal.Comparable.cs new file mode 100644 index 0000000..c566ca8 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Comparable.cs @@ -0,0 +1,98 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Compares two values and returns an integer that indicates + /// whether the left-hand value is less than, equal to, or greater than the right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns a value that indicates the relative order of the objects being compared. + public static int Compare(BigDecimal left, BigDecimal right) + { + return BigDecimalOrdinalityComparer.Default.Compare(left, right); + } + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates + /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the + /// other object. + /// + /// An object to compare with this instance. + /// Returns a value that indicates the relative order of the objects being compared. + public int CompareTo(object? obj) + { + return BigDecimalOrdinalityComparer.Default.Compare(this, obj); + } + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates + /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the + /// other object. + /// + /// An object to compare with this instance. + /// Returns a value that indicates the relative order of the objects being compared. + public int CompareTo(BigDecimal other) + { + return Compare(this, other); + } + + /// + /// Determines whether the left-hand value is greater than the right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the left-hand operand is greater than right-hand operand; otherwise, . + public static bool operator >(BigDecimal left, BigDecimal right) + { + return BigDecimalOrdinalityComparer.Default.IsGreaterThan(left, right); + } + + /// + /// Determines whether the left-hand value is greater than or equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the left-hand operand is greater than or equal to the right-hand operand; otherwise, . + public static bool operator >=(BigDecimal left, BigDecimal right) + { + return BigDecimalOrdinalityComparer.Default.IsGreaterThanOrEqual(left, right); + } + + /// + /// Determines whether the left-hand value is less than right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the left-hand operand is less than the right-hand operand; otherwise, . + public static bool operator <(BigDecimal left, BigDecimal right) + { + return BigDecimalOrdinalityComparer.Default.IsLessThan(left, right); + } + + /// + /// Determines whether the left-hand value is less than or equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the left-hand operand is less than or equal to the right-hand operand; otherwise, . + public static bool operator <=(BigDecimal left, BigDecimal right) + { + return BigDecimalOrdinalityComparer.Default.IsLessThanOrEqual(left, right); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Constants.cs b/OnixLabs.Numerics/BigDecimal.Constants.cs new file mode 100644 index 0000000..9bc5240 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Constants.cs @@ -0,0 +1,93 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Gets a negative one -1 value. + /// + public static BigDecimal NegativeOne => -1; + + /// + /// Gets a zero 0 value. + /// + public static BigDecimal Zero => 0; + + /// + /// Gets a one 1 value. + /// + public static BigDecimal One => 1; + + /// + /// Gets a two 2 value. + /// + public static BigDecimal Two => 2; + + /// + /// Gets a ten 10 value. + /// + public static BigDecimal Ten => 10; + + /// Gets the natural logarithmic base, specified by the constant, e. + /// This value is accurate to a precision of 1001 decimal digits. + public static BigDecimal E => Parse("2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354"); + + /// Gets the ratio of the circumference of a circle to its diameter, specified by the constant, π. + /// This value is accurate to a precision of 1001 decimal digits. + public static BigDecimal Pi => Parse("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989"); + + /// Gets the number of radians in one turn, specified by the constant, τ. + /// This value is accurate to a precision of 1001 decimal digits. + public static BigDecimal Tau => Parse("6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696506842341359642961730265646132941876892191011644634507188162569622349005682054038770422111192892458979098607639288576219513318668922569512964675735663305424038182912971338469206972209086532964267872145204982825474491740132126311763497630418419256585081834307287357851807200226610610976409330427682939038830232188661145407315191839061843722347638652235862102370961489247599254991347037715054497824558763660238982596673467248813132861720427898927904494743814043597218874055410784343525863535047693496369353388102640011362542905271216555715426855155792183472743574429368818024499068602930991707421015845593785178470840399122242580439217280688363196272595495426199210374144226999999967459560999021194634656321926371900489189106938166052850446165066893700705238623763420200062756775057731750664167628412343553382946071965069808575109374623191257277647075751875039155637155610643424536132260038557532223918184328403979"); + + /// + /// Gets the default number format. + /// + private const string DefaultNumberFormat = "G"; + + /// + /// Gets the default number styles for parsing decimal values. + /// + private const NumberStyles DefaultNumberStyles = NumberStyles.Any; + + /// + /// Gets the default culture for formatting and parsing operations. + /// + private static readonly CultureInfo DefaultCulture = CultureInfo.CurrentCulture; + + /// + /// Gets the magnitude by which a remainder must be multiplied in order to obtain ten digits of precision when rounding. + /// + private const long RoundingMagnitude = 10_000_000_000; + + /// + /// Gets the radix, or base, for the type. + /// + static int INumberBase.Radix => 10; + + /// + /// Gets the additive identity for the type, which is zero. + /// + static BigDecimal IAdditiveIdentity.AdditiveIdentity => Zero; + + /// + /// Gets the multiplicative identity for the type, which is one. + /// + static BigDecimal IMultiplicativeIdentity.MultiplicativeIdentity => One; +} diff --git a/OnixLabs.Numerics/BigDecimal.Convert.cs b/OnixLabs.Numerics/BigDecimal.Convert.cs new file mode 100644 index 0000000..88b5046 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Convert.cs @@ -0,0 +1,232 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + static bool INumberBase.TryConvertFromChecked(TOther value, out BigDecimal result) + { + return TryConvertFrom(value, out result); + } + + static bool INumberBase.TryConvertFromSaturating(TOther value, out BigDecimal result) + { + return TryConvertFrom(value, out result); + } + + static bool INumberBase.TryConvertFromTruncating(TOther value, out BigDecimal result) + { + return TryConvertFrom(value, out result); + } + + static bool INumberBase.TryConvertToChecked(BigDecimal value, [MaybeNullWhen(false)] out TOther result) + { + return TryConvertTo(value, out result); + } + + static bool INumberBase.TryConvertToSaturating(BigDecimal value, [MaybeNullWhen(false)] out TOther result) + { + return TryConvertTo(value, out result); + } + + static bool INumberBase.TryConvertToTruncating(BigDecimal value, [MaybeNullWhen(false)] out TOther result) + { + return TryConvertTo(value, out result); + } + + private static bool TryConvertFrom(TOther value, out BigDecimal result) where TOther : INumberBase + { + if (typeof(TOther) == typeof(sbyte)) + { + result = (sbyte)(object)value; + return true; + } + + if (typeof(TOther) == typeof(byte)) + { + result = (byte)(object)value; + return true; + } + + if (typeof(TOther) == typeof(short)) + { + result = (short)(object)value; + return true; + } + + if (typeof(TOther) == typeof(ushort)) + { + result = (ushort)(object)value; + return true; + } + + if (typeof(TOther) == typeof(int)) + { + result = (int)(object)value; + return true; + } + + if (typeof(TOther) == typeof(uint)) + { + result = (uint)(object)value; + return true; + } + + if (typeof(TOther) == typeof(long)) + { + result = (long)(object)value; + return true; + } + + if (typeof(TOther) == typeof(ulong)) + { + result = (ulong)(object)value; + return true; + } + + if (typeof(TOther) == typeof(Int128)) + { + result = (Int128)(object)value; + return true; + } + + if (typeof(TOther) == typeof(UInt128)) + { + result = (UInt128)(object)value; + return true; + } + + if (typeof(TOther) == typeof(BigInteger)) + { + result = (BigInteger)(object)value; + return true; + } + + if (typeof(TOther) == typeof(decimal)) + { + result = (decimal)(object)value; + return true; + } + + if (typeof(TOther) == typeof(double)) + { + result = (double)(object)value; + return true; + } + + if (typeof(TOther) == typeof(float)) + { + result = (float)(object)value; + return true; + } + + result = default; + return false; + } + + private static bool TryConvertTo(BigDecimal value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase + { + if (typeof(TOther) == typeof(sbyte)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(byte)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(short)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(ushort)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(int)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(uint)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(long)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(ulong)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(Int128)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(UInt128)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(BigInteger)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(decimal)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(double)) + { + result = (TOther)(object)value; + return true; + } + + if (typeof(TOther) == typeof(float)) + { + result = (TOther)(object)value; + return true; + } + + result = default; + return false; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Convertible.Explicit.cs b/OnixLabs.Numerics/BigDecimal.Convertible.Explicit.cs new file mode 100644 index 0000000..8ff95cd --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Convertible.Explicit.cs @@ -0,0 +1,199 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator BigInteger(BigDecimal value) + { + return value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator sbyte(BigDecimal value) + { + CheckIntegerOverflow(value, sbyte.MinValue, sbyte.MaxValue); + return (sbyte)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator byte(BigDecimal value) + { + CheckIntegerOverflow(value, byte.MinValue, byte.MaxValue); + return (byte)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator short(BigDecimal value) + { + CheckIntegerOverflow(value, short.MinValue, short.MaxValue); + return (short)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator ushort(BigDecimal value) + { + CheckIntegerOverflow(value, ushort.MinValue, ushort.MaxValue); + return (ushort)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator int(BigDecimal value) + { + CheckIntegerOverflow(value, int.MinValue, int.MaxValue); + return (int)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator uint(BigDecimal value) + { + CheckIntegerOverflow(value, uint.MinValue, uint.MaxValue); + return (uint)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator long(BigDecimal value) + { + CheckIntegerOverflow(value, long.MinValue, long.MaxValue); + return (long)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator ulong(BigDecimal value) + { + CheckIntegerOverflow(value, ulong.MinValue, ulong.MaxValue); + return (ulong)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator Int128(BigDecimal value) + { + CheckIntegerOverflow(value, Int128.MinValue, Int128.MaxValue); + return (Int128)value.number.Integer; + } + + /// + /// Converts the integral value of the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the integral value of the specified value. + public static explicit operator UInt128(BigDecimal value) + { + CheckIntegerOverflow(value, UInt128.MinValue, UInt128.MaxValue); + return (UInt128)value.number.Integer; + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value the specified value. + public static explicit operator float(BigDecimal value) + { + if (value < float.MinValue || value > float.MaxValue) + throw new OverflowException($"Value was either too large or too small for the specified type: {nameof(Single)}."); + + return Convert.ToSingle(value.ToString("E")); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value the specified value. + public static explicit operator double(BigDecimal value) + { + if (value < double.MinValue || value > double.MaxValue) + throw new OverflowException($"Value was either too large or too small for the specified type: {nameof(Double)}."); + + return Convert.ToDouble(value.ToString("E")); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value the specified value. + public static explicit operator decimal(BigDecimal value) + { + if (value < decimal.MinValue || value > decimal.MaxValue) + throw new OverflowException($"Value was either too large or too small for the specified type: {nameof(Decimal)}."); + + return Convert.ToDecimal(value.ToString("E")); + } + + /// + /// Checks whether the integral value of the specified + /// is within the bounds of the specified minimum and maximum values. + /// + /// The value to check. + /// The minimum bound value. + /// The maximum bound value. + /// The underlying value. + /// if the integral value of the specified + /// value is not within the bounds of the specified minimum and maximum value. + private static void CheckIntegerOverflow(BigDecimal value, T min, T max) where T : IBinaryInteger + { + BigInteger checkedMin = BigInteger.CreateChecked(min); + BigInteger checkedMax = BigInteger.CreateChecked(max); + + if (value.number.Integer < checkedMin || value.number.Integer > checkedMax) + throw new OverflowException($"Value was either too large or too small for the specified type: {typeof(T).Name}."); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Convertible.Implicit.cs b/OnixLabs.Numerics/BigDecimal.Convertible.Implicit.cs new file mode 100644 index 0000000..b9d0857 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Convertible.Implicit.cs @@ -0,0 +1,161 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(BigInteger value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(sbyte value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(byte value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(short value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(ushort value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(int value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(uint value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(long value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(ulong value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(Int128 value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(UInt128 value) + { + return new BigDecimal(value, default, ScaleMode.Integral); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(float value) + { + return new BigDecimal(value); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(double value) + { + return new BigDecimal(value); + } + + /// + /// Converts the specified value to a value. + /// + /// The value to convert. + /// Returns a value representing the specified value. + public static implicit operator BigDecimal(decimal value) + { + return new BigDecimal(value); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Convertible.cs b/OnixLabs.Numerics/BigDecimal.Convertible.cs new file mode 100644 index 0000000..c7bef84 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Convertible.cs @@ -0,0 +1,190 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Gets the for the current instance. + /// + /// Returns the enumerated constant that is the of the class or value type that implements this interface. + TypeCode IConvertible.GetTypeCode() + { + return TypeCode.Object; + } + + /// + /// Converts the value of this instance to an equivalent 8-bit signed integer using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns an 8-bit signed integer equivalent to the value of this instance. + sbyte IConvertible.ToSByte(IFormatProvider? provider) + { + return (sbyte)this; + } + + /// + /// Converts the value of this instance to an equivalent 8-bit unsigned integer using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns an 8-bit unsigned integer equivalent to the value of this instance. + byte IConvertible.ToByte(IFormatProvider? provider) + { + return (byte)this; + } + + /// + /// Converts the value of this instance to an equivalent 16-bit signed integer using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a 16-bit signed integer equivalent to the value of this instance. + short IConvertible.ToInt16(IFormatProvider? provider) + { + return (short)this; + } + + /// + /// Converts the value of this instance to an equivalent 32-bit signed integer using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a 32-bit signed integer equivalent to the value of this instance. + int IConvertible.ToInt32(IFormatProvider? provider) + { + return (int)this; + } + + /// + /// Converts the value of this instance to an equivalent 64-bit signed integer using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a 64-bit signed integer equivalent to the value of this instance. + long IConvertible.ToInt64(IFormatProvider? provider) + { + return (long)this; + } + + /// + /// Converts the value of this instance to an equivalent 16-bit unsigned integer using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a 16-bit unsigned integer equivalent to the value of this instance. + ushort IConvertible.ToUInt16(IFormatProvider? provider) + { + return (ushort)this; + } + + /// + /// Converts the value of this instance to an equivalent 32-bit unsigned integer using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a 32-bit unsigned integer equivalent to the value of this instance. + uint IConvertible.ToUInt32(IFormatProvider? provider) + { + return (uint)this; + } + + /// + /// Converts the value of this instance to an equivalent 64-bit unsigned integer using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a 64-bit unsigned integer equivalent to the value of this instance. + ulong IConvertible.ToUInt64(IFormatProvider? provider) + { + return (ulong)this; + } + + /// + /// Converts the value of this instance to an equivalent single-precision floating-point number using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a single-precision floating-point number equivalent to the value of this instance. + float IConvertible.ToSingle(IFormatProvider? provider) + { + return (float)this; + } + + /// + /// Converts the value of this instance to an equivalent double-precision floating-point number using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a double-precision floating-point number equivalent to the value of this instance. + double IConvertible.ToDouble(IFormatProvider? provider) + { + return (double)this; + } + + /// + /// Converts the value of this instance to an equivalent number using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a number equivalent to the value of this instance. + decimal IConvertible.ToDecimal(IFormatProvider? provider) + { + return (decimal)this; + } + + /// + /// Converts the value of this instance to an equivalent using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a instance equivalent to the value of this instance. + DateTime IConvertible.ToDateTime(IFormatProvider? provider) + { + return DateTime.FromBinary((long)UnscaledValue); + } + + /// + /// Converts the value of this instance to an equivalent value using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a value equivalent to the value of this instance. + bool IConvertible.ToBoolean(IFormatProvider? provider) + { + return !IsZero(this); + } + + /// + /// Converts the value of this instance to an equivalent Unicode character using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a Unicode character equivalent to the value of this instance. + char IConvertible.ToChar(IFormatProvider? provider) + { + return (char)this; + } + + /// + /// Converts the value of this instance to an equivalent using the specified culture-specific formatting information. + /// + /// An interface implementation that supplies culture-specific formatting information. + /// Returns a instance equivalent to the value of this instance. + string IConvertible.ToString(IFormatProvider? provider) + { + return ToString(DefaultNumberFormat, provider); + } + + /// + /// Converts the value of this instance to an of the specified that has an equivalent value, using the specified culture-specific formatting information. + /// + /// The to which the value of this instance is converted + /// An interface implementation that supplies culture-specific formatting information. + /// Returns an instance of type conversionType whose value is equivalent to the value of this instance. + object IConvertible.ToType(Type conversionType, IFormatProvider? provider) + { + return Convert.ChangeType(this, conversionType, provider); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Equatable.cs b/OnixLabs.Numerics/BigDecimal.Equatable.cs new file mode 100644 index 0000000..72ec86c --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Equatable.cs @@ -0,0 +1,120 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Compares two instances of to determine whether their values are equal. + /// This method implements the comparer. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the two specified instances are equal; otherwise, . + public static bool Equals(BigDecimal left, BigDecimal right) + { + return Equals(left, right, BigDecimalEqualityComparer.Strict); + } + + /// + /// Compares two instances of to determine whether their values are equal. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// The equality comparer to use to determine equality. + /// Returns if the two specified instances are equal; otherwise, . + public static bool Equals(BigDecimal left, BigDecimal right, BigDecimalEqualityComparer comparer) + { + return comparer.Equals(left, right); + } + + /// + /// Compares the current instance of with the specified other instance of . + /// This method implements the comparer. + /// + /// The other instance of to compare with the current instance. + /// Returns if the current instance is equal to the specified other instance; otherwise, . + public bool Equals(BigDecimal other) + { + return Equals(this, other); + } + + /// + /// Compares the current instance of with the specified other instance of . + /// + /// The other instance of to compare with the current instance. + /// The equality comparer to use to determine equality. + /// Returns if the current instance is equal to the specified other instance; otherwise, . + public bool Equals(BigDecimal other, BigDecimalEqualityComparer comparer) + { + return Equals(this, other, comparer); + } + + /// + /// Checks for equality between this instance and another object. + /// This method implements the comparer. + /// + /// The object to check for equality. + /// Returns if the object is equal to this instance; otherwise, . + public override bool Equals(object? obj) + { + return obj is BigDecimal other && Equals(other); + } + + /// + /// Checks for equality between this instance and another object. + /// + /// The object to check for equality. + /// The equality comparer to use to determine equality. + /// Returns if the object is equal to this instance; otherwise, . + public bool Equals(object? obj, BigDecimalEqualityComparer comparer) + { + return obj is BigDecimal other && Equals(other, comparer); + } + + /// + /// Serves as a hash code function for this instance. + /// This method implements the comparer. + /// + /// A hash code for this instance. + public override int GetHashCode() + { + return BigDecimalEqualityComparer.Strict.GetHashCode(this); + } + + /// + /// Compares two instances of to determine whether their values are equal. + /// This method implements the comparer. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the two specified instances are equal; otherwise, . + public static bool operator ==(BigDecimal left, BigDecimal right) + { + return Equals(left, right, BigDecimalEqualityComparer.Semantic); + } + + /// + /// Compares two instances of to determine whether their values are not equal. + /// This method implements the comparer. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the two specified instances are not equal; otherwise, . + public static bool operator !=(BigDecimal left, BigDecimal right) + { + return Equals(left, right, BigDecimalEqualityComparer.Semantic); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Format.cs b/OnixLabs.Numerics/BigDecimal.Format.cs new file mode 100644 index 0000000..9d7ea0d --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Format.cs @@ -0,0 +1,43 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Tries to format the value of the current instance into the provided span of characters. + /// + /// The span in which to write this instance's value formatted as a span of characters. + /// When this method returns, contains the number of characters that were written in . + /// A span containing the characters that represent a standard or custom format string that defines the acceptable format for . + /// An optional object that supplies culture-specific formatting information for . + /// Returns if the formatting was successful; otherwise, . + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + string formatted = ToString(format, provider); + + if (formatted.Length > destination.Length) + { + charsWritten = 0; + return false; + } + + formatted.AsSpan().CopyTo(destination); + charsWritten = formatted.Length; + return true; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Get.cs b/OnixLabs.Numerics/BigDecimal.Get.cs new file mode 100644 index 0000000..ed81ddb --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Get.cs @@ -0,0 +1,40 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + int IFloatingPoint.GetExponentByteCount() + { + return sizeof(int); + } + + int IFloatingPoint.GetExponentShortestBitLength() + { + return sizeof(int) - int.LeadingZeroCount(number.Exponent); + } + + int IFloatingPoint.GetSignificandBitLength() + { + return number.Significand.ToByteArray().Length * 8; + } + + int IFloatingPoint.GetSignificandByteCount() + { + return number.Significand.ToByteArray().Length; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Is.cs b/OnixLabs.Numerics/BigDecimal.Is.cs new file mode 100644 index 0000000..4a4631a --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Is.cs @@ -0,0 +1,190 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Determines if a value represents an integral number. + /// + /// The value to be checked. + /// Returns if the value is an integer; otherwise, . + public static bool IsInteger(BigDecimal value) + { + return value.number.Fraction == BigInteger.Zero; + } + + /// + /// Determines if a value represents an even integral number. + /// + /// The value to be checked. + /// Returns if the value is an even integer; otherwise, . + public static bool IsEvenInteger(BigDecimal value) + { + return IsInteger(value) && BigInteger.IsEvenInteger(value.number.Integer); + } + + /// + /// Determines if a value represents an odd integral number. + /// + /// The value to be checked. + /// Returns if the value is an odd integer; otherwise, . + public static bool IsOddInteger(BigDecimal value) + { + return IsInteger(value) && BigInteger.IsOddInteger(value.number.Integer); + } + + /// + /// Determines if a value is negative. + /// + /// The value to be checked. + /// Returns if the value is negative; otherwise, . + public static bool IsNegative(BigDecimal value) + { + return BigInteger.IsNegative(value.UnscaledValue); + } + + /// + /// Determines if a value is positive. + /// + /// The value to be checked. + /// Returns if the value is positive; otherwise, . + public static bool IsPositive(BigDecimal value) + { + return BigInteger.IsPositive(value.UnscaledValue); + } + + /// + /// Determines if a value is zero. + /// + /// The value to be checked. + /// Returns if the value is zero; otherwise, . + public static bool IsZero(BigDecimal value) + { + return value.UnscaledValue == BigInteger.Zero; + } + + /// + /// Determines if a value is in its canonical representation. + /// + /// The value to be checked. + /// Returns if value is in its canonical representation; otherwise, . + static bool INumberBase.IsCanonical(BigDecimal value) + { + return true; + } + + /// + /// Determines if a value represents a complex number. + /// + /// The value to be checked. + /// Returns if value is a complex number; otherwise, . + static bool INumberBase.IsComplexNumber(BigDecimal value) + { + return false; + } + + /// + /// Determines if a value is finite. + /// + /// The value to be checked. + /// Returns if value is finite; otherwise, . + static bool INumberBase.IsFinite(BigDecimal value) + { + return true; + } + + /// + /// Determines if a value represents a pure imaginary number. + /// + /// The value to be checked. + /// Returns if value is a pure imaginary number; otherwise, . + static bool INumberBase.IsImaginaryNumber(BigDecimal value) + { + return false; + } + + /// + /// Determines if a value is infinite. + /// + /// The value to be checked. + /// Returns if value is infinite; otherwise, . + static bool INumberBase.IsInfinity(BigDecimal value) + { + return false; + } + + /// + /// Determines if a value is NaN. + /// + /// The value to be checked. + /// Returns if value is NaN; otherwise, . + static bool INumberBase.IsNaN(BigDecimal value) + { + return false; + } + + /// + /// Determines if a value is negative infinity. + /// + /// The value to be checked. + /// Returns if value is negative infinity; otherwise, . + static bool INumberBase.IsNegativeInfinity(BigDecimal value) + { + return false; + } + + /// + /// Determines if a value is normal. + /// + /// The value to be checked. + /// Returns if value is normal; otherwise, . + static bool INumberBase.IsNormal(BigDecimal value) + { + return true; + } + + /// + /// Determines if a value is positive infinity. + /// + /// The value to be checked. + /// Returns if value is positive infinity; otherwise, . + static bool INumberBase.IsPositiveInfinity(BigDecimal value) + { + return false; + } + + /// + /// Determines if a value represents a real number. + /// + /// The value to be checked. + /// Returns if value is a real number; otherwise, + static bool INumberBase.IsRealNumber(BigDecimal value) + { + return true; + } + + /// + /// Determines if a value is subnormal. + /// + /// The value to be checked. + /// Returns if value is subnormal; otherwise, . + static bool INumberBase.IsSubnormal(BigDecimal value) + { + return false; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Parse.cs b/OnixLabs.Numerics/BigDecimal.Parse.cs new file mode 100644 index 0000000..5219347 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Parse.cs @@ -0,0 +1,169 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Globalization; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Parses the specified value into a value. + /// + /// The value to parse. + /// An object that provides culture-specific information about the specified value. + /// Returns a new instance parsed from the specified value. + public static BigDecimal Parse(string value, IFormatProvider? provider = null) + { + return Parse(value.AsSpan(), provider); + } + + /// + /// Parses the specified value into a value. + /// + /// The value to parse. + /// A bitwise combination of number styles that can be present in the specified value. + /// An object that provides culture-specific information about the specified value. + /// Returns a new instance parsed from the specified value. + public static BigDecimal Parse(string value, NumberStyles style, IFormatProvider? provider = null) + { + return Parse(value.AsSpan(), style, provider); + } + + /// + /// Parses the specified value into a value. + /// + /// The value to parse. + /// An object that provides culture-specific information about the specified value. + /// Returns a new instance parsed from the specified value. + public static BigDecimal Parse(ReadOnlySpan value, IFormatProvider? provider = null) + { + return Parse(value, DefaultNumberStyles, provider); + } + + /// + /// Parses the specified value into a value. + /// + /// The value to parse. + /// A bitwise combination of number styles that can be present in the specified value. + /// An object that provides culture-specific information about the specified value. + /// Returns a new instance parsed from the specified value. + public static BigDecimal Parse(ReadOnlySpan value, NumberStyles style, IFormatProvider? provider = null) + { + CultureInfo info = provider as CultureInfo ?? DefaultCulture; + if (TryParse(value, style, info, out BigDecimal result)) return result; + throw new FormatException($"The input string '{value}' was not in a correct format."); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(string? value, out BigDecimal result) + { + return TryParse(value.AsSpan(), out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// An object that provides culture-specific information about the specified value. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(string? value, IFormatProvider? provider, out BigDecimal result) + { + return TryParse(value.AsSpan(), provider, out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// A bitwise combination of number styles that can be present in the specified value. + /// An object that provides culture-specific information about the specified value. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(string? value, NumberStyles style, IFormatProvider? provider, out BigDecimal result) + { + return TryParse(value.AsSpan(), style, provider, out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, out BigDecimal result) + { + return TryParse(value, DefaultCulture, out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// An object that provides culture-specific information about the specified value. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, IFormatProvider? provider, out BigDecimal result) + { + return TryParse(value, DefaultNumberStyles, provider, out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// A bitwise combination of number styles that can be present in the specified value. + /// An object that provides culture-specific information about the specified value. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, NumberStyles style, IFormatProvider? provider, out BigDecimal result) + { + CultureInfo info = provider as CultureInfo ?? DefaultCulture; + NumberInfoParser parser = new(style, info); + + if (parser.TryParse(value, out NumberInfo numberInfo)) + { + result = new BigDecimal(numberInfo.UnscaledValue, numberInfo.Scale); + return true; + } + + result = default; + return false; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.To.cs b/OnixLabs.Numerics/BigDecimal.To.cs new file mode 100644 index 0000000..a8a4aab --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.To.cs @@ -0,0 +1,103 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Globalization; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// + /// Gets a representing the current . + /// + /// Returns a representing the current . + public NumberInfo ToNumberInfo() + { + return number; + } + + /// + /// Formats the value of the current instance using the default format. + /// + /// The value of the current instance in the default format. + public override string ToString() + { + return ToString(DefaultNumberFormat, DefaultCulture); + } + + /// + /// Formats the value of the current instance using the specified format. + /// + /// The format to use, or null to use the default format. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + return ToString((format ?? DefaultNumberFormat).AsSpan(), formatProvider); + } + + /// + /// Formats the value of the current instance using the specified format. + /// + /// The format to use, or null to use the default format. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(ReadOnlySpan format, IFormatProvider? formatProvider = null) + { + CultureInfo info = formatProvider as CultureInfo ?? DefaultCulture; + + if (!TryGetScaledNumberInfo(format, info.NumberFormat, out NumberInfo value)) return format.ToString(); + + NumberInfoFormatter formatter = new(value, info, ['C', 'D', 'E', 'F', 'G', 'N', 'P']); + return formatter.Format(format); + } + + /// + /// Attempts to obtain a value that is scaled by either using a custom + /// scale that is specified by the format, or by using the default scale for the specified format. + /// + /// The format specifier from which to obtain the desired scale. + /// The number format of the target culture that determines default scales for specific formats. + /// The value with a correctly applied scale. + /// Returns if the scale is applied correctly; otherwise, . + private bool TryGetScaledNumberInfo(ReadOnlySpan format, NumberFormatInfo numberFormat, out NumberInfo result) + { + const MidpointRounding mode = MidpointRounding.AwayFromZero; + char specifier = format.IsEmpty || format.IsWhiteSpace() ? NumberInfoFormatter.DefaultFormat : format[0]; + + if (format.Length > 1) + { + if (!int.TryParse(format[1..], out int scale)) + { + result = default; + return false; + } + + result = SetScale(scale).number; + return true; + } + + result = char.ToUpperInvariant(specifier) switch + { + 'C' => SetScale(numberFormat.CurrencyDecimalDigits, mode).ToNumberInfo(), + 'F' => SetScale(numberFormat.NumberDecimalDigits, mode).ToNumberInfo(), + 'N' => SetScale(numberFormat.NumberDecimalDigits, mode).ToNumberInfo(), + 'P' => Multiply(this, 100).SetScale(numberFormat.PercentDecimalDigits, mode).ToNumberInfo(), + _ => ToNumberInfo() + }; + + return true; + } +} diff --git a/OnixLabs.Numerics/BigDecimal.Write.cs b/OnixLabs.Numerics/BigDecimal.Write.cs new file mode 100644 index 0000000..3a1d89b --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.Write.cs @@ -0,0 +1,76 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Buffers.Binary; +using System.Numerics; + +namespace OnixLabs.Numerics; + +public readonly partial struct BigDecimal +{ + /// Tries to write the current exponent, in big-endian format, to a given span. + /// The span to which the current exponent should be written. + /// When this method returns, contains the number of bytes written to . + /// Returns if the exponent was successfully written to ; otherwise, . + bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + { + const int size = sizeof(int); + + if (destination.Length >= size && BinaryPrimitives.TryWriteInt32BigEndian(destination, number.Exponent)) + { + bytesWritten = size; + return true; + } + + bytesWritten = 0; + return false; + } + + /// Tries to write the current exponent, in little-endian format, to a given span. + /// The span to which the current exponent should be written. + /// When this method returns, contains the number of bytes written to . + /// Returns if the exponent was successfully written to ; otherwise, . + bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + { + const int size = sizeof(int); + + if (destination.Length >= size && BinaryPrimitives.TryWriteInt32LittleEndian(destination, number.Exponent)) + { + bytesWritten = size; + return true; + } + + bytesWritten = 0; + return false; + } + + /// Tries to write the current significand, in big-endian format, to a given span. + /// The span to which the current significand should be written. + /// When this method returns, contains the number of bytes written to . + /// Returns if the significand was successfully written to ; otherwise, . + bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + { + return number.Significand.TryWriteBytes(destination, out bytesWritten, isBigEndian: true); + } + + /// Tries to write the current significand, in little-endian format, to a given span. + /// The span to which the current significand should be written. + /// When this method returns, contains the number of bytes written to . + /// Returns if the significand was successfully written to ; otherwise, . + bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + { + return number.Significand.TryWriteBytes(destination, out bytesWritten); + } +} diff --git a/OnixLabs.Numerics/BigDecimal.cs b/OnixLabs.Numerics/BigDecimal.cs new file mode 100644 index 0000000..2de9c4d --- /dev/null +++ b/OnixLabs.Numerics/BigDecimal.cs @@ -0,0 +1,84 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Numerics; +using OnixLabs.Core; + +namespace OnixLabs.Numerics; + +/// +/// Represents an arbitrarily large signed decimal. +/// +public readonly partial struct BigDecimal : IFloatingPoint, IValueEquatable, IValueComparable, IConvertible +{ + /// + /// The underlying that represents the current value. + /// + private readonly NumberInfo number; + + /// + /// Initializes a new instance of the struct. + /// + /// The unscaled integer value from which to construct a value. + /// The scale of the value. + /// The scale mode that determines how the specified value should be scaled. + public BigDecimal(BigInteger value, int scale = default, ScaleMode mode = default) + { + Require(scale >= 0, "Scale must be greater than or equal to zero.", nameof(scale)); + RequireIsDefined(mode, nameof(mode)); + number = value.ToNumberInfo(scale, mode); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The floating-point value from which to construct a value. + /// The conversion mode that determines whether the floating-point value should be converted from its binary or decimal representation. + public BigDecimal(float value, ConversionMode mode = default) + { + RequireIsDefined(mode, nameof(mode)); + number = value.ToNumberInfo(mode); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The floating-point value from which to construct a value. + /// The conversion mode that determines whether the floating-point value should be converted from its binary or decimal representation. + public BigDecimal(double value, ConversionMode mode = default) + { + RequireIsDefined(mode, nameof(mode)); + number = value.ToNumberInfo(mode); + } + + /// + /// Initializes a new instance of the struct. + /// + /// The decimal value from which to construct a value. + public BigDecimal(decimal value) + { + number = value.ToNumberInfo(); + } + + /// + /// Gets the unscaled value of the current value. + /// + public BigInteger UnscaledValue => number.UnscaledValue; + + /// + /// Gets the scale of the current value. + /// + public int Scale => number.Scale; +} diff --git a/OnixLabs.Numerics/BigDecimalEqualityComparer.cs b/OnixLabs.Numerics/BigDecimalEqualityComparer.cs new file mode 100644 index 0000000..ac29578 --- /dev/null +++ b/OnixLabs.Numerics/BigDecimalEqualityComparer.cs @@ -0,0 +1,117 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections; +using System.Collections.Generic; + +namespace OnixLabs.Numerics; + +/// +/// Represents an equality comparer for comparing values. +/// +public abstract class BigDecimalEqualityComparer : IEqualityComparer, IEqualityComparer +{ + /// + /// Gets an equality comparer that strictly compares values. + /// + public static readonly BigDecimalEqualityComparer Strict = new BigDecimalStrictEqualityComparer(); + + /// + /// Gets an equality comparer that semantically compares values. + /// + public static readonly BigDecimalEqualityComparer Semantic = new BigDecimalSemanticEqualityComparer(); + + /// + /// Prevents a default instance of the class from being created. + /// + private BigDecimalEqualityComparer() + { + } + + /// Determines whether the specified values are equal. + /// The first object of type to compare. + /// The second object of type to compare. + /// Returns if the specified values are equal; otherwise, . + public abstract bool Equals(BigDecimal x, BigDecimal y); + + /// Returns a hash code for the specified value. + /// The value for which a hash code is to be returned. + /// Returns a hash code for the specified value. + public abstract int GetHashCode(BigDecimal obj); + + /// Determines whether the specified objects are equal. + /// The first object to compare. + /// The second object to compare. + /// Returns if the specified objects are both of type and are equal; otherwise, . + public new bool Equals(object? x, object? y) + { + return x is BigDecimal left && y is BigDecimal right && Equals(left, right); + } + + /// Returns a hash code for the specified object. + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode(object obj) + { + return obj is BigDecimal value ? GetHashCode(value) : obj.GetHashCode(); + } + + /// + /// Represents an equality comparer that compares values using strict equality. + /// Strict equality is determined by comparing and properties. + /// + private sealed class BigDecimalStrictEqualityComparer : BigDecimalEqualityComparer + { + /// Determines whether the specified values are equal. + /// The first object of type to compare. + /// The second object of type to compare. + /// Returns if the specified values are equal; otherwise, . + public override bool Equals(BigDecimal x, BigDecimal y) + { + return NumberInfoEqualityComparer.Strict.Equals(x.ToNumberInfo(), y.ToNumberInfo()); + } + + /// Returns a hash code for the specified object. + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public override int GetHashCode(BigDecimal obj) + { + return NumberInfoEqualityComparer.Strict.GetHashCode(obj.ToNumberInfo()); + } + } + + /// + /// Represents an equality comparer that compares values using semantic equality. + /// Semantic equality is determined by comparing that values are equivalent, even when their scale differs. + /// + private sealed class BigDecimalSemanticEqualityComparer : BigDecimalEqualityComparer + { + /// Determines whether the specified values are equal. + /// The first object of type to compare. + /// The second object of type to compare. + /// Returns if the specified values are equal; otherwise, . + public override bool Equals(BigDecimal x, BigDecimal y) + { + return NumberInfoEqualityComparer.Semantic.Equals(x.ToNumberInfo(), y.ToNumberInfo()); + } + + /// Returns a hash code for the specified object. + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public override int GetHashCode(BigDecimal obj) + { + return NumberInfoEqualityComparer.Semantic.GetHashCode(obj.ToNumberInfo()); + } + } +} diff --git a/OnixLabs.Numerics/BigDecimalOrdinalityComparer.cs b/OnixLabs.Numerics/BigDecimalOrdinalityComparer.cs new file mode 100644 index 0000000..b3e67ce --- /dev/null +++ b/OnixLabs.Numerics/BigDecimalOrdinalityComparer.cs @@ -0,0 +1,114 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace OnixLabs.Numerics; + +/// +/// Represents an ordinal comparer for comparing values. +/// +public sealed class BigDecimalOrdinalityComparer : IComparer, IComparer +{ + /// + /// Gets the default ordinal comparer for comparing values. + /// + public static readonly BigDecimalOrdinalityComparer Default = new(); + + /// + /// Prevents a default instance of the class from being created. + /// + private BigDecimalOrdinalityComparer() + { + } + + /// Compares two values and returns a value indicating whether one is less than, equal to, or greater than the other. + /// The first value to compare. + /// The second value to compare. + /// Returns a signed integer that indicates the relative order of the values being compared. + public int Compare(BigDecimal x, BigDecimal y) + { + return NumberInfoOrdinalityComparer.Default.Compare(x.ToNumberInfo(), y.ToNumberInfo()); + } + + /// Compares two values and returns a value indicating whether one is less than, equal to, or greater than the other. + /// The first value to compare. + /// The second value to compare. + /// Returns a signed integer that indicates the relative order of the values being compared. + /// If either or are not of type . + public int Compare(object? x, object? y) + { + if (x is not BigDecimal left) throw new ArgumentException($"Argument must be of type {nameof(BigDecimal)}", nameof(x)); + if (y is not BigDecimal right) throw new ArgumentException($"Argument must be of type {nameof(BigDecimal)}", nameof(y)); + + return Compare(left, right); + } + + /// + /// Determines whether the left-hand value is equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is equal to the right-hand value; otherwise, . + public bool IsEqual(BigDecimal left, BigDecimal right) + { + return Compare(left, right) is 0; + } + + /// + /// Determines whether the left-hand value is greater than the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is greater than the right-hand value; otherwise, . + public bool IsGreaterThan(BigDecimal left, BigDecimal right) + { + return Compare(left, right) is 1; + } + + /// + /// Determines whether the left-hand value is greater than, or equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is greater than, or equal to the right-hand value; otherwise, . + public bool IsGreaterThanOrEqual(BigDecimal left, BigDecimal right) + { + return Compare(left, right) is 0 or 1; + } + + /// + /// Determines whether the left-hand value is less than the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is less than the right-hand value; otherwise, . + public bool IsLessThan(BigDecimal left, BigDecimal right) + { + return Compare(left, right) is -1; + } + + /// + /// Determines whether the left-hand value is less than, or equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is less than, or equal to the right-hand value; otherwise, . + public bool IsLessThanOrEqual(BigDecimal left, BigDecimal right) + { + return Compare(left, right) is -1 or 0; + } +} diff --git a/OnixLabs.Core/Text/Base64.Empty.cs b/OnixLabs.Numerics/ConversionMode.cs similarity index 58% rename from OnixLabs.Core/Text/Base64.Empty.cs rename to OnixLabs.Numerics/ConversionMode.cs index 53a24fa..cbdd173 100644 --- a/OnixLabs.Core/Text/Base64.Empty.cs +++ b/OnixLabs.Numerics/ConversionMode.cs @@ -1,33 +1,31 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; +namespace OnixLabs.Numerics; -namespace OnixLabs.Core.Text; - -public readonly partial struct Base64 +/// +/// Specifies IEEE 754 binary floating-point conversion modes. +/// +public enum ConversionMode { /// - /// Gets an empty Base-64 value. + /// Specifies that IEEE 754 binary floating-point values will be converted from their decimal representation. /// - public static readonly Base64 Empty; + Decimal, /// - /// Initializes static members of the class. + /// Specifies that IEEE 754 binary floating-point values will be converted from their binary representation. /// - static Base64() - { - Empty = FromByteArray(Array.Empty()); - } + Binary } diff --git a/OnixLabs.Numerics/GenericMath.cs b/OnixLabs.Numerics/GenericMath.cs new file mode 100644 index 0000000..fa663a3 --- /dev/null +++ b/OnixLabs.Numerics/GenericMath.cs @@ -0,0 +1,63 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; +using System.Numerics; + +namespace OnixLabs.Numerics; + +/// +/// Provides generic mathematical functions. +/// +public static class GenericMath +{ + /// + /// Computes the delta, or difference between the specified numbers. + /// + /// The left-hand number from which to compute the delta. + /// The right-hand number from which to compute the delta. + /// The underlying type. + /// Returns the delta, or difference between the specified numbers. + public static T Delta(T left, T right) where T : INumberBase + { + return T.Abs(left - right); + } + + /// + /// Obtains the length of the integral component of the specified value. + /// + /// The value from which to obtain the length of the integral component. + /// The underlying type. + /// Returns the length of the integral component of the specified value. + public static int IntegerLength(T value) where T : INumberBase + { + BigInteger integer = BigInteger.Abs(BigInteger.CreateTruncating(value)); + return integer.ToString("G", CultureInfo.InvariantCulture).Length; + } + + /// + /// Obtains the minimum and maximum values from the specified left-hand and right-hand values. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// The underlying type. + /// Returns the minimum and maximum values from the specified left-hand and right-hand values. + public static (T Min, T Max) MinMax(T left, T right) where T : INumber + { + T min = T.Min(left, right); + T max = T.Max(left, right); + + return (min, max); + } +} diff --git a/OnixLabs.Numerics/Ieee754Converter.cs b/OnixLabs.Numerics/Ieee754Converter.cs new file mode 100644 index 0000000..2a4a7fd --- /dev/null +++ b/OnixLabs.Numerics/Ieee754Converter.cs @@ -0,0 +1,228 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Globalization; +using System.Linq; +using System.Numerics; +using OnixLabs.Core; +using OnixLabs.Core.Linq; + +namespace OnixLabs.Numerics; + +/// +/// Represents conversion of IEEE 754 binary floating-point numbers. +/// +internal static class Ieee754Converter +{ + /// + /// Converts an IEEE 754 single-precision binary floating-point number. + /// + /// The value to convert. + /// The mode that specifies whether the value should be converted using its binary or decimal representation. + /// Returns a containing the unscaled value and scale. + public static NumberInfo Convert(float value, ConversionMode mode) + { + RequireRealNumber(value); + RequireIsDefined(mode, nameof(mode)); + + if (IsZeroOrOne(value, out NumberInfo result)) return result; + + return mode switch + { + ConversionMode.Binary => ConvertFromBinary(value), + ConversionMode.Decimal => ConvertFromDecimal(value), + _ => throw new ArgumentOutOfRangeException(nameof(mode)) + }; + } + + /// + /// Converts an IEEE 754 double-precision binary floating-point number. + /// + /// The value to convert. + /// The mode that specifies whether the value should be converted using its binary or decimal representation. + /// Returns a containing the unscaled value and scale. + public static NumberInfo Convert(double value, ConversionMode mode) + { + RequireRealNumber(value); + RequireIsDefined(mode, nameof(mode)); + + if (IsZeroOrOne(value, out NumberInfo result)) return result; + + return mode switch + { + ConversionMode.Binary => ConvertFromBinary(value), + ConversionMode.Decimal => ConvertFromDecimal(value), + _ => throw new ArgumentOutOfRangeException(nameof(mode)) + }; + } + + /// + /// Converts an IEEE 754 single-precision binary floating-point number using its binary representation. + /// + /// The value to convert. + /// Returns a containing the unscaled value and scale. + private static NumberInfo ConvertFromBinary(float value) + { + const int significandBits = 23; + const int exponentBits = 8; + const int significandMask = (1 << significandBits) - 1; + const int exponentMask = (1 << exponentBits) - 1; + + int bits = BitConverter.SingleToInt32Bits(value); + int significand = bits & significandMask; + int exponent = (bits >> significandBits) & exponentMask; + bool denormalizedExponent = exponent is 0; + + exponent -= denormalizedExponent ? 126 : 127; + exponent -= significandBits; + + if (!denormalizedExponent) significand |= 1 << significandBits; + + while (exponent < 0 && (significand & 1) is 0) + { + exponent++; + significand >>= 1; + } + + BigInteger unscaledValue = bits < 0 ? -significand : significand; + int scale = 0; + + if (exponent < 0) + { + scale = int.Abs(exponent); + unscaledValue *= BigInteger.Pow(5, scale); + } + else + { + unscaledValue <<= exponent; + } + + return new NumberInfo(unscaledValue, scale); + } + + /// + /// Converts an IEEE 754 double-precision binary floating-point number using its binary representation. + /// + /// The value to convert. + /// Returns a containing the unscaled value and scale. + private static NumberInfo ConvertFromBinary(double value) + { + const int significandBits = 52; + const int exponentBits = 11; + const long significandMask = (1L << significandBits) - 1; + const long exponentMask = (1L << exponentBits) - 1; + + long bits = BitConverter.DoubleToInt64Bits(value); + long significand = bits & significandMask; + long exponent = (bits >> significandBits) & exponentMask; + bool denormalizedExponent = exponent is 0; + + exponent -= denormalizedExponent ? 1022 : 1023; + exponent -= significandBits; + + if (!denormalizedExponent) significand |= 1L << significandBits; + + while (exponent < 0 && (significand & 1) is 0) + { + exponent++; + significand >>= 1; + } + + BigInteger unscaledValue = bits < 0 ? -significand : significand; + int scale = 0; + + if (exponent < 0) + { + scale = (int)long.Abs(exponent); + unscaledValue *= BigInteger.Pow(5, scale); + } + else + { + unscaledValue <<= (int)exponent; + } + + return new NumberInfo(unscaledValue, scale); + } + + /// + /// Converts an IEEE 754 binary floating-point number using its decimal representation. + /// + /// The value to convert. + /// The underlying type of the value. + /// Returns a containing the unscaled value and scale. + private static NumberInfo ConvertFromDecimal(T value) where T : IBinaryFloatingPointIeee754 + { + const char zero = '0'; + const string format = "R"; + const string delimiter = "E"; + const StringComparison comparison = StringComparison.CurrentCultureIgnoreCase; + + int exponent = int.Parse(value.ToString("E", NumberFormatInfo.CurrentInfo).SubstringAfterLast(delimiter, comparison: comparison)); + + string digits = value + .ToString(format, NumberFormatInfo.CurrentInfo) + .SubstringBeforeFirst(delimiter, comparison: comparison) + .Where(char.IsDigit) + .JoinToString(string.Empty) + .PadRight(int.Max(0, exponent + 1), zero); + + BigInteger unscaledValue = BigInteger.Parse(digits) * T.Sign(value); + int scale = BigInteger.Abs(unscaledValue).ToString().Length - (exponent + 1); + + return new NumberInfo(unscaledValue, scale); + } + + /// + /// Determines whether the specified IEEE 754 binary floating-point number is zero or one. + /// + /// The value to check. + /// The result if the number is zero or one. + /// The underlying type of the value. + /// Returns if the specified IEEE 754 binary floating-point number is zero or one; otherwise, . + private static bool IsZeroOrOne(T value, out NumberInfo result) where T : IBinaryFloatingPointIeee754 + { + if (value == T.Zero || value == T.NegativeZero) + { + result = NumberInfo.Zero; + return true; + } + + if (value == T.One) + { + result = NumberInfo.One; + return true; + } + + if (value == T.NegativeOne) + { + result = NumberInfo.NegativeOne; + return true; + } + + result = default; + return false; + } + + /// + /// Requires that the specified value is a real number. + /// + /// The value to check. + /// The underlying type of the <see cref="IBinaryFloatingPointIeee754{TSelf}"/> value. + private static void RequireRealNumber(T value) where T : IBinaryFloatingPointIeee754 + { + Require(!T.IsNaN(value), "Value must not be NaN.", nameof(value)); + Require(!T.IsInfinity(value), "Value must not be infinite.", nameof(value)); + } +} diff --git a/OnixLabs.Numerics/NumberInfo.Comparable.cs b/OnixLabs.Numerics/NumberInfo.Comparable.cs new file mode 100644 index 0000000..8c71bd4 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfo.Comparable.cs @@ -0,0 +1,98 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Numerics; + +public readonly partial struct NumberInfo +{ + /// + /// Compares two values and returns an integer that indicates + /// whether the left-hand value is less than, equal to, or greater than the right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns a value that indicates the relative order of the objects being compared. + public static int Compare(NumberInfo left, NumberInfo right) + { + return NumberInfoOrdinalityComparer.Default.Compare(left, right); + } + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates + /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the + /// other object. + /// + /// An object to compare with this instance. + /// Returns a value that indicates the relative order of the objects being compared. + public int CompareTo(NumberInfo other) + { + return Compare(this, other); + } + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates + /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the + /// other object. + /// + /// An object to compare with this instance. + /// Returns a value that indicates the relative order of the objects being compared. + public int CompareTo(object? obj) + { + return NumberInfoOrdinalityComparer.Default.Compare(this, obj); + } + + /// + /// Determines whether the left-hand value is greater than the right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the left-hand operand is greater than right-hand operand; otherwise, . + public static bool operator >(NumberInfo left, NumberInfo right) + { + return NumberInfoOrdinalityComparer.Default.IsGreaterThan(left, right); + } + + /// + /// Determines whether the left-hand value is greater than or equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the left-hand operand is greater than or equal to the right-hand operand; otherwise, . + public static bool operator >=(NumberInfo left, NumberInfo right) + { + return NumberInfoOrdinalityComparer.Default.IsGreaterThanOrEqual(left, right); + } + + /// + /// Determines whether the left-hand value is less than right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the left-hand operand is less than the right-hand operand; otherwise, . + public static bool operator <(NumberInfo left, NumberInfo right) + { + return NumberInfoOrdinalityComparer.Default.IsLessThan(left, right); + } + + /// + /// Determines whether the left-hand value is less than or equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the left-hand operand is less than or equal to the right-hand operand; otherwise, . + public static bool operator <=(NumberInfo left, NumberInfo right) + { + return NumberInfoOrdinalityComparer.Default.IsLessThanOrEqual(left, right); + } +} diff --git a/OnixLabs.Numerics/NumberInfo.Constants.cs b/OnixLabs.Numerics/NumberInfo.Constants.cs new file mode 100644 index 0000000..324c2d3 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfo.Constants.cs @@ -0,0 +1,50 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; + +namespace OnixLabs.Numerics; + +public readonly partial struct NumberInfo +{ + /// + /// Gets a instance representing zero. + /// + public static NumberInfo Zero => new(0, 0); + + /// + /// Gets a instance representing one. + /// + public static NumberInfo One => new(1, 0); + + /// + /// Gets a instance representing negative one. + /// + public static NumberInfo NegativeOne => new(-1, 0); + + /// + /// Gets the default number format. + /// + private const string DefaultNumberFormat = "G"; + + /// + /// Gets the default number styles for parsing decimal values. + /// + private const NumberStyles DefaultNumberStyles = NumberStyles.Any; + + /// + /// Gets the default culture for formatting and parsing operations. + /// + private static readonly CultureInfo DefaultCulture = CultureInfo.CurrentCulture; +} diff --git a/OnixLabs.Numerics/NumberInfo.Equatable.cs b/OnixLabs.Numerics/NumberInfo.Equatable.cs new file mode 100644 index 0000000..fba2bad --- /dev/null +++ b/OnixLabs.Numerics/NumberInfo.Equatable.cs @@ -0,0 +1,122 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Numerics; + +public readonly partial struct NumberInfo +{ + /// + /// Compares two instances of to determine whether their values are equal. + /// This method implements the comparer. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the two specified instances are equal; otherwise, . + public static bool Equals(NumberInfo left, NumberInfo right) + { + return Equals(left, right, NumberInfoEqualityComparer.Strict); + } + + /// + /// Compares two instances of to determine whether their values are equal. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// The equality comparer to use to determine equality. + /// Returns if the two specified instances are equal; otherwise, . + public static bool Equals(NumberInfo left, NumberInfo right, NumberInfoEqualityComparer comparer) + { + return comparer.Equals(left, right); + } + + /// + /// Compares the current instance of with the specified other instance of . + /// This method implements the comparer. + /// + /// The other instance of to compare with the current instance. + /// Returns if the current instance is equal to the specified other instance; otherwise, . + public bool Equals(NumberInfo other) + { + return Equals(this, other); + } + + /// + /// Compares the current instance of with the specified other instance of . + /// + /// The other instance of to compare with the current instance. + /// The equality comparer to use to determine equality. + /// Returns if the current instance is equal to the specified other instance; otherwise, . + public bool Equals(NumberInfo other, NumberInfoEqualityComparer comparer) + { + return Equals(this, other, comparer); + } + + /// + /// Checks for equality between this instance and another object. + /// This method implements the comparer. + /// + /// The object to check for equality. + /// Returns if the object is equal to this instance; otherwise, . + public override bool Equals(object? obj) + { + return obj is NumberInfo other && Equals(other); + } + + /// + /// Checks for equality between this instance and another object. + /// + /// The object to check for equality. + /// The equality comparer to use to determine equality. + /// Returns if the object is equal to this instance; otherwise, . + public bool Equals(object? obj, NumberInfoEqualityComparer comparer) + { + return obj is NumberInfo other && Equals(other, comparer); + } + + /// + /// Serves as a hash code function for this instance. + /// This method implements the comparer. + /// + /// A hash code for this instance. + public override int GetHashCode() + { + return NumberInfoEqualityComparer.Strict.GetHashCode(this); + } + + /// + /// Compares two instances of to determine whether their values are equal. + /// This method implements the comparer. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the two specified instances are equal; otherwise, . + public static bool operator ==(NumberInfo left, NumberInfo right) + { + return Equals(left, right, NumberInfoEqualityComparer.Semantic); + } + + /// + /// Compares two instances of to determine whether their values are not equal. + /// This method implements the comparer. + /// + /// The left-hand value to compare. + /// The right-hand value to compare. + /// Returns if the two specified instances are not equal; otherwise, . + public static bool operator !=(NumberInfo left, NumberInfo right) + { + return Equals(left, right, NumberInfoEqualityComparer.Semantic); + } +} diff --git a/OnixLabs.Numerics/NumberInfo.Parse.cs b/OnixLabs.Numerics/NumberInfo.Parse.cs new file mode 100644 index 0000000..51c760d --- /dev/null +++ b/OnixLabs.Numerics/NumberInfo.Parse.cs @@ -0,0 +1,161 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Globalization; + +namespace OnixLabs.Numerics; + +public readonly partial struct NumberInfo +{ + /// + /// Parses the specified value into a value. + /// + /// The value to parse. + /// An object that provides culture-specific information about the specified value. + /// Returns a new instance parsed from the specified value. + public static NumberInfo Parse(string value, IFormatProvider? provider = null) + { + return Parse(value.AsSpan(), provider); + } + + /// + /// Parses the specified value into a value. + /// + /// The value to parse. + /// A bitwise combination of number styles that can be present in the specified value. + /// An object that provides culture-specific information about the specified value. + /// Returns a new instance parsed from the specified value. + public static NumberInfo Parse(string value, NumberStyles style, IFormatProvider? provider = null) + { + return Parse(value.AsSpan(), style, provider); + } + + /// + /// Parses the specified value into a value. + /// + /// The value to parse. + /// An object that provides culture-specific information about the specified value. + /// Returns a new instance parsed from the specified value. + public static NumberInfo Parse(ReadOnlySpan value, IFormatProvider? provider = null) + { + return Parse(value, DefaultNumberStyles, provider); + } + + /// + /// Parses the specified value into a value. + /// + /// The value to parse. + /// A bitwise combination of number styles that can be present in the specified value. + /// An object that provides culture-specific information about the specified value. + /// Returns a new instance parsed from the specified value. + public static NumberInfo Parse(ReadOnlySpan value, NumberStyles style, IFormatProvider? provider = null) + { + CultureInfo info = provider as CultureInfo ?? DefaultCulture; + if (TryParse(value, style, info, out NumberInfo result)) return result; + throw new FormatException($"The input string '{value}' was not in a correct format."); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(string? value, out NumberInfo result) + { + return TryParse(value.AsSpan(), out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// An object that provides culture-specific information about the specified value. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(string? value, IFormatProvider? provider, out NumberInfo result) + { + return TryParse(value.AsSpan(), provider, out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// A bitwise combination of number styles that can be present in the specified value. + /// An object that provides culture-specific information about the specified value. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(string? value, NumberStyles style, IFormatProvider? provider, out NumberInfo result) + { + return TryParse(value.AsSpan(), style, provider, out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, out NumberInfo result) + { + return TryParse(value, DefaultCulture, out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// An object that provides culture-specific information about the specified value. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, IFormatProvider? provider, out NumberInfo result) + { + return TryParse(value, DefaultNumberStyles, provider, out result); + } + + /// + /// Tries to parse the specified value into a value. + /// + /// The value to parse. + /// A bitwise combination of number styles that can be present in the specified value. + /// An object that provides culture-specific information about the specified value. + /// + /// On return, contains the result of parsing the specified value, + /// or the default value in the event that the specified value could not be parsed. + /// + /// Returns if the specified value was parsed successfully; otherwise, . + public static bool TryParse(ReadOnlySpan value, NumberStyles style, IFormatProvider? provider, out NumberInfo result) + { + CultureInfo info = provider as CultureInfo ?? DefaultCulture; + NumberInfoParser parser = new(style, info); + return parser.TryParse(value, out result); + } +} diff --git a/OnixLabs.Numerics/NumberInfo.To.cs b/OnixLabs.Numerics/NumberInfo.To.cs new file mode 100644 index 0000000..ddf9be3 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfo.To.cs @@ -0,0 +1,54 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Globalization; + +namespace OnixLabs.Numerics; + +public readonly partial struct NumberInfo +{ + /// + /// Formats the value of the current instance using the default format. + /// + /// The value of the current instance in the default format. + public override string ToString() + { + return ToString(DefaultNumberFormat, DefaultCulture); + } + + /// + /// Formats the value of the current instance using the specified format. + /// + /// The format to use, or null to use the default format. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(string? format, IFormatProvider? formatProvider = null) + { + return ToString((format ?? DefaultNumberFormat).AsSpan(), formatProvider); + } + + /// + /// Formats the value of the current instance using the specified format. + /// + /// The format to use, or null to use the default format. + /// The provider to use to format the value. + /// The value of the current instance in the specified format. + public string ToString(ReadOnlySpan format, IFormatProvider? formatProvider = null) + { + CultureInfo info = formatProvider as CultureInfo ?? DefaultCulture; + NumberInfoFormatter formatter = new(this, info, ['E', 'G']); + return formatter.Format(format); + } +} diff --git a/OnixLabs.Numerics/NumberInfo.cs b/OnixLabs.Numerics/NumberInfo.cs new file mode 100644 index 0000000..5a47e61 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfo.cs @@ -0,0 +1,115 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Numerics; +using OnixLabs.Core; + +namespace OnixLabs.Numerics; + +/// +/// Represents component information about rational numbers. +/// +public readonly partial struct NumberInfo : + IValueEquatable, + IValueComparable, + ISpanParsable, + IFormattable +{ + /// + /// Prevents a default instance of the struct from being created. + /// + /// The unscaled value of the represented number. + /// The scale of the represented number. + internal NumberInfo(BigInteger unscaledValue, int scale) + { + UnscaledValue = unscaledValue; + Scale = scale; + } + + /// + /// Gets the unscaled value of the represented number. + /// The unscaled value is represented as a signed value, including any trailing zeros. + /// If the represented number contains a fractional component, the trailing zeros are considered significant. + /// + public BigInteger UnscaledValue { get; } + + /// + /// Gets the scale of the represented number. + /// The scale is represented as a positive or neutral integer. + /// The scale indicates how many digits from the right-hand side of the represent the fractional component of the represented number. + /// + public int Scale { get; } + + /// + /// Gets the significand of the represented number. + /// The significand is represented as a signed value, excluding any trailing zeros. + /// If the represented number contains a fractional component, the trailing zeros are considered insignificant and must be calculated from the of the represented number. + /// + public BigInteger Significand + { + get + { + if (UnscaledValue == BigInteger.Zero) return BigInteger.Zero; + + BigInteger significand = UnscaledValue; + int exponent = 0; + + while (significand % BigInteger.Pow(10, exponent) == BigInteger.Zero) exponent++; + return significand / BigInteger.Pow(10, --exponent); + } + } + + /// + /// Gets the exponent of the represented number. + /// The exponent is represented as a positive, negative or neutral number. + /// + public int Exponent + { + get + { + if (UnscaledValue == BigInteger.Zero) return 0; + if (Scale == 0) return Precision - GenericMath.IntegerLength(Significand); + return -(Scale - (GenericMath.IntegerLength(UnscaledValue) - GenericMath.IntegerLength(Significand))); + } + } + + /// + /// Gets the precision of the represented number. + /// The precision is represented as a positive value, indicating how many significant digits the represented number contains. + /// If the represented number's or contain fewer digits that the , then trailing zeros are considered significant. + /// + public int Precision => int.Max(GenericMath.IntegerLength(UnscaledValue), Scale + 1); + + /// + /// Gets the sign of the represented number. + /// The sign is represented as negative one for negative numbers, positive one for positive numbers; otherwise, zero. + /// + public int Sign => UnscaledValue.Sign; + + /// + /// Gets the scale factor of the represented number. + /// + internal BigInteger ScaleFactor => BigInteger.Pow(10, Scale); + + /// + /// Gets the integral component of the represented number. + /// + internal BigInteger Integer => UnscaledValue / ScaleFactor; + + /// + /// Gets the fractional component of the represented number. + /// + internal BigInteger Fraction => BigInteger.Abs(UnscaledValue - Integer * ScaleFactor); +} diff --git a/OnixLabs.Numerics/NumberInfoEqualityComparer.cs b/OnixLabs.Numerics/NumberInfoEqualityComparer.cs new file mode 100644 index 0000000..7181e25 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoEqualityComparer.cs @@ -0,0 +1,118 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace OnixLabs.Numerics; + +/// +/// Represents an equality comparer for comparing values. +/// +public abstract class NumberInfoEqualityComparer : IEqualityComparer, IEqualityComparer +{ + /// + /// Gets a that strictly compares values. + /// + public static readonly NumberInfoEqualityComparer Strict = new NumberInfoStrictEqualityComparer(); + + /// + /// Gets a that semantically compares values. + /// + public static readonly NumberInfoEqualityComparer Semantic = new NumberInfoSemanticEqualityComparer(); + + /// + /// Prevents a default instance of the class from being created. + /// + private NumberInfoEqualityComparer() + { + } + + /// Determines whether the specified values are equal. + /// The first object of type to compare. + /// The second object of type to compare. + /// Returns if the specified values are equal; otherwise, . + public abstract bool Equals(NumberInfo x, NumberInfo y); + + /// Returns a hash code for the specified value. + /// The value for which a hash code is to be returned. + /// Returns a hash code for the specified value. + public abstract int GetHashCode(NumberInfo obj); + + /// Determines whether the specified objects are equal. + /// The first object to compare. + /// The second object to compare. + /// Returns if the specified objects are both of type and are equal; otherwise, . + public new bool Equals(object? x, object? y) + { + return x is NumberInfo left && y is NumberInfo right && Equals(left, right); + } + + /// Returns a hash code for the specified object. + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode(object obj) + { + return obj is NumberInfo value ? GetHashCode(value) : obj.GetHashCode(); + } + + /// + /// Represents an equality comparer that compares values using strict equality. + /// Strict equality is determined by comparing and properties. + /// + private sealed class NumberInfoStrictEqualityComparer : NumberInfoEqualityComparer + { + /// Determines whether the specified values are equal. + /// The first object of type to compare. + /// The second object of type to compare. + /// Returns if the specified values are equal; otherwise, . + public override bool Equals(NumberInfo x, NumberInfo y) + { + return x.UnscaledValue == y.UnscaledValue && x.Scale == y.Scale; + } + + /// Returns a hash code for the specified value. + /// The value for which a hash code is to be returned. + /// Returns a hash code for the specified value. + public override int GetHashCode(NumberInfo obj) + { + return HashCode.Combine(obj.UnscaledValue, obj.Scale); + } + } + + /// + /// Represents an equality comparer that compares values using semantic equality. + /// Semantic equality is determined by comparing and properties. + /// + private sealed class NumberInfoSemanticEqualityComparer : NumberInfoEqualityComparer + { + /// Determines whether the specified values are equal. + /// The first object of type to compare. + /// The second object of type to compare. + /// Returns if the specified values are equal; otherwise, . + public override bool Equals(NumberInfo x, NumberInfo y) + { + return NumberInfoOrdinalityComparer.Default.IsEqual(x, y); + } + + /// Returns a hash code for the specified value. + /// The value for which a hash code is to be returned. + /// Returns a hash code for the specified value. + public override int GetHashCode(NumberInfo obj) + { + return HashCode.Combine(obj.Significand, obj.Exponent); + } + } +} diff --git a/OnixLabs.Numerics/NumberInfoFormatter.FormatCurrency.cs b/OnixLabs.Numerics/NumberInfoFormatter.FormatCurrency.cs new file mode 100644 index 0000000..f159b14 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoFormatter.FormatCurrency.cs @@ -0,0 +1,119 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using OnixLabs.Core.Text; + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoFormatter +{ + /// + /// Formats the current value using the currency format. + /// + private void FormatCurrency() + { + FormatInteger(numberFormat.CurrencyGroupSizes, numberFormat.CurrencyGroupSeparator); + FormatFraction(numberFormat.CurrencyDecimalSeparator); + FormatCurrencyPositivePattern(); + FormatCurrencyNegativePattern(); + } + + /// + /// Applies currency positive pattern formatting to the current being formatted. + /// + private void FormatCurrencyPositivePattern() + { + if (BigInteger.IsNegative(value.UnscaledValue)) return; + + switch (numberFormat.CurrencyPositivePattern) + { + case 0: // $n + builder.Prepend(numberFormat.CurrencySymbol); + break; + case 1: // n$ + builder.Append(numberFormat.CurrencySymbol); + break; + case 2: // $ n + builder.Prepend(numberFormat.CurrencySymbol, Whitespace); + break; + case 3: // n $ + builder.Append(Whitespace, numberFormat.CurrencySymbol); + break; + } + } + + /// + /// Applies currency negative pattern formatting to the current being formatted. + /// + private void FormatCurrencyNegativePattern() + { + if (BigInteger.IsPositive(value.UnscaledValue)) return; + + switch (numberFormat.CurrencyNegativePattern) + { + case 0: // ($n) + builder.Prepend(numberFormat.CurrencySymbol).Wrap(LeadingParenthesis, TrailingParenthesis); + break; + case 1: // -$n + builder.Prepend(numberFormat.NegativeSign, numberFormat.CurrencySymbol); + break; + case 2: // $-n + builder.Prepend(numberFormat.CurrencySymbol, numberFormat.NegativeSign); + break; + case 3: // $n- + builder.Prepend(numberFormat.CurrencySymbol).Append(numberFormat.NegativeSign); + break; + case 4: // (n$) + builder.Append(numberFormat.CurrencySymbol).Wrap(LeadingParenthesis, TrailingParenthesis); + break; + case 5: // -n$ + builder.Prepend(numberFormat.NegativeSign).Append(numberFormat.CurrencySymbol); + break; + case 6: // n-$ + builder.Append(numberFormat.NegativeSign, numberFormat.CurrencySymbol); + break; + case 7: // n$- + builder.Append(numberFormat.CurrencySymbol, numberFormat.NegativeSign); + break; + case 8: // -n $ + builder.Prepend(numberFormat.NegativeSign).Append(Whitespace, numberFormat.CurrencySymbol); + break; + case 9: // -$ n + builder.Prepend(numberFormat.NegativeSign, numberFormat.CurrencySymbol, Whitespace); + break; + case 10: // n $- + builder.Append(Whitespace, numberFormat.CurrencySymbol, numberFormat.NegativeSign); + break; + case 11: // $ n- + builder.Prepend(numberFormat.CurrencySymbol, Whitespace).Append(numberFormat.NegativeSign); + break; + case 12: // $ -n + builder.Prepend(numberFormat.CurrencySymbol, Whitespace, numberFormat.NegativeSign); + break; + case 13: // n- $ + builder.Append(numberFormat.NegativeSign, Whitespace, numberFormat.CurrencySymbol); + break; + case 14: // ($ n) + builder.Prepend(numberFormat.CurrencySymbol, Whitespace).Wrap(LeadingParenthesis, TrailingParenthesis); + break; + case 15: // (n $) + builder.Append(Whitespace, numberFormat.CurrencySymbol).Wrap(LeadingParenthesis, TrailingParenthesis); + break; + case 16: // $- n + builder.Prepend(numberFormat.CurrencySymbol, numberFormat.NegativeSign, Whitespace); + break; + } + } +} diff --git a/OnixLabs.Numerics/NumberInfoFormatter.FormatDecimal.cs b/OnixLabs.Numerics/NumberInfoFormatter.FormatDecimal.cs new file mode 100644 index 0000000..e9fd2d4 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoFormatter.FormatDecimal.cs @@ -0,0 +1,28 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoFormatter +{ + /// + /// Formats the current value using the decimal format. + /// + private void FormatDecimal() + { + FormatInteger(numberFormat.NumberGroupSizes, numberFormat.NumberGroupSeparator); + FormatFraction(numberFormat.NumberDecimalSeparator); + FormatNumberNegativePattern(); + } +} diff --git a/OnixLabs.Numerics/NumberInfoFormatter.FormatExponential.cs b/OnixLabs.Numerics/NumberInfoFormatter.FormatExponential.cs new file mode 100644 index 0000000..50a2a90 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoFormatter.FormatExponential.cs @@ -0,0 +1,42 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using OnixLabs.Core.Text; + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoFormatter +{ + /// + /// Formats the current value using the exponential (otherwise known as scientific notation) format. + /// + /// The exponentiation specifier, which is either an uppercase E, or lowercase e. + private void FormatExponential(char specifier) + { + builder.Append(BigInteger.Abs(value.UnscaledValue)); + + if (value == NumberInfo.Zero) return; + + int exponent = builder.Length - value.Scale - 1; + builder.Trim('0').Insert(1, numberFormat.NumberDecimalSeparator).TrimEnd(numberFormat.NumberDecimalSeparator); + + if (exponent == 0) return; + + string sign = exponent > 0 ? numberFormat.PositiveSign : numberFormat.NegativeSign; + builder.Append(specifier, sign, int.Abs(exponent)); + + if (value.UnscaledValue < BigInteger.Zero) builder.Prepend(numberFormat.NegativeSign); + } +} diff --git a/OnixLabs.Core/Text/Base16.Empty.cs b/OnixLabs.Numerics/NumberInfoFormatter.FormatFixed.cs similarity index 61% rename from OnixLabs.Core/Text/Base16.Empty.cs rename to OnixLabs.Numerics/NumberInfoFormatter.FormatFixed.cs index c059b38..34b7747 100644 --- a/OnixLabs.Core/Text/Base16.Empty.cs +++ b/OnixLabs.Numerics/NumberInfoFormatter.FormatFixed.cs @@ -1,33 +1,28 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; +namespace OnixLabs.Numerics; -namespace OnixLabs.Core.Text; - -public readonly partial struct Base16 +internal sealed partial class NumberInfoFormatter { /// - /// Gets an empty Base-16 value. - /// - public static readonly Base16 Empty; - - /// - /// Initializes static members of the class. + /// Formats the current value using the fixed format. /// - static Base16() + private void FormatFixed() { - Empty = FromByteArray(Array.Empty()); + FormatInteger([]); + FormatFraction(numberFormat.NumberDecimalSeparator); + FormatNumberNegativePattern(); } } diff --git a/OnixLabs.Core/Text/Base32.Empty.cs b/OnixLabs.Numerics/NumberInfoFormatter.FormatGeneral.cs similarity index 61% rename from OnixLabs.Core/Text/Base32.Empty.cs rename to OnixLabs.Numerics/NumberInfoFormatter.FormatGeneral.cs index 6f5006d..5d51bb3 100644 --- a/OnixLabs.Core/Text/Base32.Empty.cs +++ b/OnixLabs.Numerics/NumberInfoFormatter.FormatGeneral.cs @@ -1,33 +1,28 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; +namespace OnixLabs.Numerics; -namespace OnixLabs.Core.Text; - -public readonly partial struct Base32 +internal sealed partial class NumberInfoFormatter { /// - /// Gets an empty Base-32 value. - /// - public static readonly Base32 Empty; - - /// - /// Initializes static members of the class. + /// Formats the current value using the general format. /// - static Base32() + private void FormatGeneral() { - Empty = FromByteArray(Array.Empty()); + FormatInteger([]); + FormatFraction(numberFormat.NumberDecimalSeparator); + FormatNumberNegativePattern(); } } diff --git a/OnixLabs.Numerics/NumberInfoFormatter.FormatNumber.cs b/OnixLabs.Numerics/NumberInfoFormatter.FormatNumber.cs new file mode 100644 index 0000000..3932b54 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoFormatter.FormatNumber.cs @@ -0,0 +1,58 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using OnixLabs.Core.Text; + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoFormatter +{ + /// + /// Formats the current value using the number format. + /// + private void FormatNumber() + { + FormatInteger(numberFormat.NumberGroupSizes, numberFormat.NumberGroupSeparator); + FormatFraction(numberFormat.NumberDecimalSeparator); + FormatNumberNegativePattern(); + } + + /// + /// Applies number negative pattern formatting to the current being formatted. + /// + private void FormatNumberNegativePattern() + { + if (BigInteger.IsPositive(value.UnscaledValue)) return; + + switch (numberFormat.NumberNegativePattern) + { + case 0: // (n) + builder.Wrap(LeadingParenthesis, TrailingParenthesis); + break; + case 1: // -n + builder.Prepend(numberFormat.NegativeSign); + break; + case 2: // - n + builder.Prepend(numberFormat.NegativeSign, Whitespace); + break; + case 3: // n- + builder.Append(numberFormat.NegativeSign); + break; + case 4: // n - + builder.Append(Whitespace, numberFormat.NegativeSign); + break; + } + } +} diff --git a/OnixLabs.Numerics/NumberInfoFormatter.FormatPercentage.cs b/OnixLabs.Numerics/NumberInfoFormatter.FormatPercentage.cs new file mode 100644 index 0000000..ab85fdb --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoFormatter.FormatPercentage.cs @@ -0,0 +1,104 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using OnixLabs.Core.Text; + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoFormatter +{ + /// + /// Formats the current value using the percentage format. + /// + private void FormatPercent() + { + FormatInteger(numberFormat.PercentGroupSizes, numberFormat.PercentGroupSeparator); + FormatFraction(numberFormat.PercentDecimalSeparator); + FormatPercentPositivePattern(); + FormatPercentNegativePattern(); + } + + /// + /// Applies percent positive pattern formatting to the current being formatted. + /// + private void FormatPercentPositivePattern() + { + if (BigInteger.IsNegative(value.UnscaledValue)) return; + + switch (numberFormat.PercentPositivePattern) + { + case 0: // n % + builder.Append(Whitespace, numberFormat.PercentSymbol); + break; + case 1: // n% + builder.Append(numberFormat.PercentSymbol); + break; + case 2: // %n + builder.Prepend(numberFormat.PercentSymbol); + break; + case 3: // % n + builder.Prepend(numberFormat.PercentSymbol, Whitespace); + break; + } + } + + /// + /// Applies percent negative pattern formatting to the current being formatted. + /// + private void FormatPercentNegativePattern() + { + if (BigInteger.IsPositive(value.UnscaledValue)) return; + + switch (numberFormat.PercentNegativePattern) + { + case 0: // -n % + builder.Prepend(numberFormat.NegativeSign).Append(Whitespace, numberFormat.PercentSymbol); + break; + case 1: // -n% + builder.Prepend(numberFormat.NegativeSign).Append(numberFormat.PercentSymbol); + break; + case 2: // -%n + builder.Prepend(numberFormat.NegativeSign, numberFormat.PercentSymbol); + break; + case 3: // %-n + builder.Prepend(numberFormat.PercentSymbol, numberFormat.NegativeSign); + break; + case 4: // %n- + builder.Prepend(numberFormat.PercentSymbol).Append(numberFormat.NegativeSign); + break; + case 5: // n-% + builder.Append(numberFormat.NegativeSign, numberFormat.PercentSymbol); + break; + case 6: // n%- + builder.Append(numberFormat.PercentSymbol, numberFormat.NegativeSign); + break; + case 7: // -% n + builder.Prepend(numberFormat.NegativeSign, numberFormat.PercentSymbol, Whitespace); + break; + case 8: // n %- + builder.Append(Whitespace, numberFormat.PercentSymbol, numberFormat.NegativeSign); + break; + case 9: // % n- + builder.Prepend(numberFormat.PercentSymbol, Whitespace).Append(numberFormat.NegativeSign); + break; + case 10: // % -n + builder.Prepend(numberFormat.PercentSymbol, Whitespace, numberFormat.NegativeSign); + break; + case 11: // n- % + builder.Append(numberFormat.NegativeSign, Whitespace, numberFormat.PercentSymbol); + break; + } + } +} diff --git a/OnixLabs.Numerics/NumberInfoFormatter.cs b/OnixLabs.Numerics/NumberInfoFormatter.cs new file mode 100644 index 0000000..d32b864 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoFormatter.cs @@ -0,0 +1,124 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Numerics; +using System.Text; +using OnixLabs.Core.Text; + +namespace OnixLabs.Numerics; + +/// +/// Represents a formatter for formatting values. +/// +/// The value to format. +/// The format provider, which should be a containing the desired format details. +/// Specifies which formats are allowed for formatting. +internal sealed partial class NumberInfoFormatter(NumberInfo value, IFormatProvider formatProvider, IEnumerable allowedFormats) +{ + internal const char DefaultFormat = 'G'; + + private const char LeadingParenthesis = '('; + private const char TrailingParenthesis = ')'; + private const char Whitespace = ' '; + + private readonly StringBuilder builder = new(); + private readonly NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(formatProvider); + private readonly IEnumerable allowedFormats = allowedFormats.Select(char.ToUpperInvariant); + + /// + /// Formats the current value using the specified format. + /// + /// The desired format of the value. + /// Returns a representation of the current value. + public string Format(ReadOnlySpan format) + { + char specifier = format.IsEmpty || format.IsWhiteSpace() ? DefaultFormat : format[0]; + char specifierUpperInvariant = char.ToUpperInvariant(specifier); + + if (!allowedFormats.Contains(specifierUpperInvariant)) return format.ToString(); + + switch (specifierUpperInvariant) + { + case 'C': + FormatCurrency(); + break; + case 'D': + FormatDecimal(); + break; + case 'E': + FormatExponential(specifier); + break; + case 'F': + FormatFixed(); + break; + case 'G': + FormatGeneral(); + break; + case 'N': + FormatNumber(); + break; + case 'P': + FormatPercent(); + break; + default: + return format.ToString(); + } + + return builder.ToString(); + } + + /// + /// Formats the integral component of the current value. + /// + /// The sizes of each number group. + /// The separator that separates each number group. + private void FormatInteger(IReadOnlyList grouping, string separator = "") + { + builder.Append(BigInteger.Abs(value.Integer)); + + if (grouping.Count == 0) return; + + int position = builder.Length - 1; + int count = 0; + int index = 0; + + while (position > 0) + { + if (char.IsDigit(builder[position])) count++; + + if (count == grouping[index]) + { + builder.Insert(position, separator); + count = 0; + + if (index < grouping.Count - 1) index++; + } + + position--; + } + } + + /// + /// Formats the fractional component of the current value. + /// + /// The separator that separates the integral and fractional components. + private void FormatFraction(string separator) + { + if (value.Scale > 0) builder.Append(separator, value.Fraction.ToString().PadLeft(value.Scale, '0')); + } +} diff --git a/OnixLabs.Numerics/NumberInfoOrdinalityComparer.cs b/OnixLabs.Numerics/NumberInfoOrdinalityComparer.cs new file mode 100644 index 0000000..a31b6e9 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoOrdinalityComparer.cs @@ -0,0 +1,119 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Numerics; + +namespace OnixLabs.Numerics; + +/// +/// Represents an ordinality comparer for comparing values. +/// +public sealed class NumberInfoOrdinalityComparer : IComparer, IComparer +{ + /// + /// Gets the default ordinal comparer for comparing values. + /// + public static readonly NumberInfoOrdinalityComparer Default = new(); + + /// + /// Prevents a default instance of the class from being created. + /// + private NumberInfoOrdinalityComparer() + { + } + + /// Compares two values and returns a value indicating whether one is less than, equal to, or greater than the other. + /// The first value to compare. + /// The second value to compare. + /// Returns a signed integer that indicates the relative order of the values being compared. + public int Compare(NumberInfo x, NumberInfo y) + { + BigInteger factor = BigInteger.Min(x.ScaleFactor, y.ScaleFactor); + BigInteger xNormalized = x.UnscaledValue * y.ScaleFactor / factor; + BigInteger yNormalized = y.UnscaledValue * x.ScaleFactor / factor; + + return xNormalized.CompareTo(yNormalized); + } + + /// Compares two values and returns a value indicating whether one is less than, equal to, or greater than the other. + /// The first value to compare. + /// The second value to compare. + /// Returns a signed integer that indicates the relative order of the values being compared. + /// If either or are not of type . + public int Compare(object? x, object? y) + { + if (x is not NumberInfo xInfo) throw new ArgumentException($"Argument must be of type {nameof(NumberInfo)}", nameof(x)); + if (y is not NumberInfo yInfo) throw new ArgumentException($"Argument must be of type {nameof(NumberInfo)}", nameof(y)); + + return Compare(xInfo, yInfo); + } + + /// + /// Determines whether the left-hand value is equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is equal to the right-hand value; otherwise, . + public bool IsEqual(NumberInfo left, NumberInfo right) + { + return Compare(left, right) is 0; + } + + /// + /// Determines whether the left-hand value is greater than the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is greater than the right-hand value; otherwise, . + public bool IsGreaterThan(NumberInfo left, NumberInfo right) + { + return Compare(left, right) is 1; + } + + /// + /// Determines whether the left-hand value is greater than, or equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is greater than, or equal to the right-hand value; otherwise, . + public bool IsGreaterThanOrEqual(NumberInfo left, NumberInfo right) + { + return Compare(left, right) is 0 or 1; + } + + /// + /// Determines whether the left-hand value is less than the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is less than the right-hand value; otherwise, . + public bool IsLessThan(NumberInfo left, NumberInfo right) + { + return Compare(left, right) is -1; + } + + /// + /// Determines whether the left-hand value is less than, or equal to the right-hand value. + /// + /// The left-hand value to compare. + /// The left-hand value to compare. + /// Returns if the left-hand value is less than, or equal to the right-hand value; otherwise, . + public bool IsLessThanOrEqual(NumberInfo left, NumberInfo right) + { + return Compare(left, right) is -1 or 0; + } +} diff --git a/OnixLabs.Numerics/NumberInfoParser.Sanitize.cs b/OnixLabs.Numerics/NumberInfoParser.Sanitize.cs new file mode 100644 index 0000000..2bc0c66 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoParser.Sanitize.cs @@ -0,0 +1,142 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using static System.Globalization.NumberStyles; + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoParser +{ + private bool TryTrimLeadingWhitespace(ref ReadOnlySpan value) + { + ReadOnlySpan result = value.TrimStart(); + + if (result.SequenceEqual(value)) return true; + if (!style.HasFlag(AllowLeadingWhite)) return false; + + value = result; + return true; + } + + private bool TryTrimTrailingWhitespace(ref ReadOnlySpan value) + { + ReadOnlySpan result = value.TrimEnd(); + + if (result.SequenceEqual(value)) return true; + if (!style.HasFlag(AllowTrailingWhite)) return false; + + value = result; + return true; + } + + private bool TryTrimLeadingCurrencySymbol(ref ReadOnlySpan value) + { + ReadOnlySpan result = value.TrimStart(numberFormat.CurrencySymbol); + + if (result.SequenceEqual(value)) return true; + if (!style.HasFlag(AllowCurrencySymbol)) return false; + + value = result; + return true; + } + + private bool TryTrimTrailingCurrencySymbol(ref ReadOnlySpan value) + { + ReadOnlySpan result = value.TrimEnd(numberFormat.CurrencySymbol); + + if (result.SequenceEqual(value)) return true; + if (!style.HasFlag(AllowCurrencySymbol)) return false; + + value = result; + return true; + } + + private bool TryTrimLeadingPositiveSign(ref ReadOnlySpan value, out bool hasLeadingPositiveSign) + { + hasLeadingPositiveSign = value.StartsWith(numberFormat.PositiveSign, Comparison); + + if (!hasLeadingPositiveSign) return true; + if (!style.HasFlag(AllowLeadingSign)) return false; + + value = value.TrimStart(numberFormat.PositiveSign); + return true; + } + + private bool TryTrimTrailingPositiveSign(ref ReadOnlySpan value, out bool hasTrailingPositiveSign) + { + hasTrailingPositiveSign = value.EndsWith(numberFormat.PositiveSign, Comparison); + + if (!hasTrailingPositiveSign) return true; + if (!style.HasFlag(AllowTrailingSign)) return false; + + value = value.TrimStart(numberFormat.PositiveSign); + return true; + } + + private bool TryTrimLeadingNegativeSign(ref ReadOnlySpan value, out bool hasLeadingNegativeSign) + { + hasLeadingNegativeSign = value.StartsWith(numberFormat.NegativeSign, Comparison); + + if (!hasLeadingNegativeSign) return true; + if (!style.HasFlag(AllowLeadingSign)) return false; + + value = value.TrimStart(numberFormat.NegativeSign); + return true; + } + + private bool TryTrimTrailingNegativeSign(ref ReadOnlySpan value, out bool hasTrailingNegativeSign) + { + hasTrailingNegativeSign = value.EndsWith(numberFormat.NegativeSign, Comparison); + + if (!hasTrailingNegativeSign) return true; + if (!style.HasFlag(AllowTrailingSign)) return false; + + value = value.TrimStart(numberFormat.NegativeSign); + return true; + } + + private bool TryTrimParentheses(ref ReadOnlySpan value, out bool hasParentheses) + { + hasParentheses = false; + bool hasLeadingParenthesis = value.StartsWith(LeadingParenthesis, Comparison); + bool hasTrailingParenthesis = value.EndsWith(TrailingParenthesis, Comparison); + bool areParenthesesAllowed = style.HasFlag(AllowParentheses); + + if (hasLeadingParenthesis && hasTrailingParenthesis && areParenthesesAllowed) + { + hasParentheses = true; + value = value.TrimStart(LeadingParenthesis).TrimEnd(TrailingParenthesis); + return true; + } + + return !hasLeadingParenthesis && !hasTrailingParenthesis; + } + + private bool TryTrimExponent(ref ReadOnlySpan value, out int exponent) + { + exponent = default; + + int index = value.IndexOf(ExponentSymbol, Comparison); + int lastIndex = value.LastIndexOf(ExponentSymbol, Comparison); + + if (index == -1) return true; + if (index != lastIndex) return false; + + ReadOnlySpan chars = value[(index + 1)..]; + value = value[..index]; + + return int.TryParse(chars, culture, out exponent) && style.HasFlag(AllowExponent); + } +} diff --git a/OnixLabs.Numerics/NumberInfoParser.SanitizeCurrency.cs b/OnixLabs.Numerics/NumberInfoParser.SanitizeCurrency.cs new file mode 100644 index 0000000..930cb4c --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoParser.SanitizeCurrency.cs @@ -0,0 +1,138 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Linq; + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoParser +{ + private bool TrySanitizeCurrency(ref ReadOnlySpan value, out int sign) + { + sign = 1; + + bool hasParentheses = false; + bool hasLeadingNegativeSign = false; + bool hasTrailingNegativeSign = false; + + if (!TryTrimLeadingWhitespace(ref value)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + + switch (numberFormat.CurrencyPositivePattern) + { + case 0: // $n + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + break; + case 1: // n$ + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + break; + case 2: // $ n + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + if (!TryTrimLeadingWhitespace(ref value)) return false; + break; + case 3: // n $ + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + break; + } + + switch (numberFormat.CurrencyNegativePattern) + { + case 0: // ($n) + if (!TryTrimParentheses(ref value, out hasParentheses)) return false; + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + break; + case 1: // -$n + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + break; + case 2: // $-n + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + break; + case 3: // $n- + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + if (!TryTrimTrailingNegativeSign(ref value, out hasTrailingNegativeSign)) return false; + break; + case 4: // (n$) + if (!TryTrimParentheses(ref value, out hasParentheses)) return false; + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + break; + case 5: // -n$ + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + break; + case 6: // n-$ + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + if (!TryTrimTrailingNegativeSign(ref value, out hasTrailingNegativeSign)) return false; + break; + case 7: // n$- + if (!TryTrimTrailingNegativeSign(ref value, out hasTrailingNegativeSign)) return false; + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + break; + case 8: // -n $ + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + break; + case 9: // -$ n + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + if (!TryTrimLeadingWhitespace(ref value)) return false; + break; + case 10: // n $- + if (!TryTrimTrailingNegativeSign(ref value, out hasTrailingNegativeSign)) return false; + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + break; + case 11: // $ n- + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + if (!TryTrimLeadingWhitespace(ref value)) return false; + if (!TryTrimTrailingNegativeSign(ref value, out hasTrailingNegativeSign)) return false; + break; + case 12: // $ -n + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + if (!TryTrimLeadingWhitespace(ref value)) return false; + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + break; + case 13: // n- $ + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + if (!TryTrimTrailingNegativeSign(ref value, out hasTrailingNegativeSign)) return false; + break; + case 14: // ($ n) + if (!TryTrimParentheses(ref value, out hasParentheses)) return false; + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + if (!TryTrimLeadingWhitespace(ref value)) return false; + break; + case 15: // (n $) + if (!TryTrimParentheses(ref value, out hasParentheses)) return false; + if (!TryTrimTrailingCurrencySymbol(ref value)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + break; + case 16: // $- n + if (!TryTrimLeadingCurrencySymbol(ref value)) return false; + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + if (!TryTrimLeadingWhitespace(ref value)) return false; + break; + } + + bool[] values = [hasParentheses, hasLeadingNegativeSign, hasTrailingNegativeSign]; + if (values.Count(value => value) > 1) return false; + + if (hasParentheses || hasLeadingNegativeSign || hasTrailingNegativeSign) sign = -1; + return true; + } +} diff --git a/OnixLabs.Numerics/NumberInfoParser.SanitizeNumber.cs b/OnixLabs.Numerics/NumberInfoParser.SanitizeNumber.cs new file mode 100644 index 0000000..55ca83a --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoParser.SanitizeNumber.cs @@ -0,0 +1,76 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Linq; + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoParser +{ + private bool TrySanitizeNumber(ref ReadOnlySpan value, out int sign, out int exponent) + { + sign = 1; + exponent = 0; + + bool hasParentheses = false; + bool hasLeadingPositiveSign = false; + bool hasLeadingNegativeSign = false; + bool hasTrailingPositiveSign = false; + bool hasTrailingNegativeSign = false; + + // Trim overall leading and trailing whitespace + if (!TryTrimLeadingWhitespace(ref value)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + + if (!TryTrimLeadingPositiveSign(ref value, out hasLeadingPositiveSign)) return false; + if (!TryTrimTrailingPositiveSign(ref value, out hasTrailingPositiveSign)) return false; + + switch (numberFormat.NumberNegativePattern) + { + case 0: // (n) + if (!TryTrimParentheses(ref value, out hasParentheses)) return false; + break; + case 1: // -n + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + break; + case 2: // - n + if (!TryTrimLeadingNegativeSign(ref value, out hasLeadingNegativeSign)) return false; + if (!TryTrimLeadingWhitespace(ref value)) return false; + break; + case 3: // n- + if (!TryTrimTrailingNegativeSign(ref value, out hasTrailingNegativeSign)) return false; + break; + case 4: + if (!TryTrimTrailingNegativeSign(ref value, out hasTrailingNegativeSign)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + break; + } + + // Trim whitespace that appears after a leading positive or negative sign + if (!TryTrimLeadingWhitespace(ref value)) return false; + if (!TryTrimTrailingWhitespace(ref value)) return false; + + if (!TryTrimExponent(ref value, out exponent)) return false; + + // Trim whitespace that appears between the number and an exponent + if (!TryTrimTrailingWhitespace(ref value)) return false; + + bool[] values = [hasParentheses, hasLeadingPositiveSign, hasLeadingNegativeSign, hasTrailingPositiveSign, hasTrailingNegativeSign]; + if (values.Count(value => value) > 1) return false; + + if (hasParentheses || hasLeadingNegativeSign || hasTrailingNegativeSign) sign = -1; + return true; + } +} diff --git a/OnixLabs.Numerics/NumberInfoParser.cs b/OnixLabs.Numerics/NumberInfoParser.cs new file mode 100644 index 0000000..1da1248 --- /dev/null +++ b/OnixLabs.Numerics/NumberInfoParser.cs @@ -0,0 +1,119 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Globalization; +using System.Numerics; +using static System.Globalization.NumberStyles; + +namespace OnixLabs.Numerics; + +internal sealed partial class NumberInfoParser(NumberStyles style, IFormatProvider culture) +{ + private const string LeadingParenthesis = "("; + private const string TrailingParenthesis = ")"; + private const string ExponentSymbol = "e"; + private const StringComparison Comparison = StringComparison.InvariantCultureIgnoreCase; + + private readonly NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(culture); + + public bool TryParse(ReadOnlySpan value, out NumberInfo result) + { + result = default; + + int sign = 1; + int exponent = 0; + + // Disallow the following number styles as they are unsupported. + if (style == None) return false; + if (style.HasFlag(AllowHexSpecifier)) return false; + if (style.HasFlag(AllowBinarySpecifier)) return false; + + // Special handling for sanitization of currency values. + if (style == Currency) + { + if (!TrySanitizeCurrency(ref value, out sign)) return false; + } + else + { + if (!TrySanitizeNumber(ref value, out sign, out exponent)) return false; + } + + // At this point, only digits, thousands and decimal separators should remain. + if (!TryGetNumberInfo(ref value, out NumberInfo rawResult)) return false; + + result = new NumberInfo(rawResult.UnscaledValue * sign, int.Max(rawResult.Scale - exponent, 0)); + return true; + } + + private bool TryGetNumberInfo(ref ReadOnlySpan value, out NumberInfo result) + { + result = default; + + BigInteger unscaledValue = BigInteger.Zero; + int scale = 0; + bool hasDecimalPoint = false; + + while (value.Length > 0) + { + if (char.IsAsciiDigit(value[0])) + { + int digit = value[0] - '0'; + unscaledValue *= 10; + unscaledValue += digit; + + if (hasDecimalPoint) scale++; + + value = value[1..]; + continue; + } + + if (value.StartsWith(numberFormat.NumberGroupSeparator)) + { + if (!style.HasFlag(AllowThousands)) return false; + value = value.TrimStart(numberFormat.NumberGroupSeparator); + continue; + } + + if (value.StartsWith(numberFormat.CurrencyGroupSeparator)) + { + if (!style.HasFlag(AllowThousands)) return false; + value = value.TrimStart(numberFormat.CurrencyGroupSeparator); + continue; + } + + if (value.StartsWith(numberFormat.NumberDecimalSeparator)) + { + if (hasDecimalPoint || !style.HasFlag(AllowDecimalPoint)) return false; + hasDecimalPoint = true; + value = value.TrimStart(numberFormat.NumberDecimalSeparator); + continue; + } + + if (value.StartsWith(numberFormat.CurrencyDecimalSeparator)) + { + if (hasDecimalPoint || !style.HasFlag(AllowDecimalPoint)) return false; + hasDecimalPoint = true; + value = value.TrimStart(numberFormat.CurrencyDecimalSeparator); + continue; + } + + // If we reach this point, the start of the string isn't an ascii digit, thousand or decimal separator, therefore false. + return false; + } + + result = new NumberInfo(unscaledValue, scale); + return true; + } +} diff --git a/OnixLabs.Numerics/NumericsExtensions.cs b/OnixLabs.Numerics/NumericsExtensions.cs new file mode 100644 index 0000000..185ba6e --- /dev/null +++ b/OnixLabs.Numerics/NumericsExtensions.cs @@ -0,0 +1,212 @@ +// Copyright © 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.ComponentModel; +using System.Numerics; + +namespace OnixLabs.Numerics; + +/// +/// Provides extension methods for numeric types. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class NumericsExtensions +{ + /// + /// Gets the minimum value of a as a . + /// + private static readonly BigInteger MinDecimal = new(decimal.MinValue); + + /// + /// Gets the maximum value of a as a . + /// + private static readonly BigInteger MaxDecimal = new(decimal.MaxValue); + + /// + /// Gets the unscaled value of the current . + /// + /// The from which to obtain an unscaled value. + /// Returns the unscaled value of the current as a . + public static BigInteger GetUnscaledValue(this decimal value) + { + const int significandSize = 13; + int[] significandBits = decimal.GetBits(value); + byte[] significandBytes = new byte[significandSize]; + Buffer.BlockCopy(significandBits, 0, significandBytes, 0, significandSize); + BigInteger result = new(significandBytes); + + return decimal.IsPositive(value) ? result : -result; + } + + /// + /// Gets the current value as an unscaled integer. + /// + /// The value to get as an unscaled integer. + /// The desired scale of the result. + /// The scale mode of the desired result. + /// The underlying type. + /// Returns an unscaled integer representation of the current value. + private static BigInteger GetUnscaledInteger(this T value, int scale, ScaleMode mode) where T : IBinaryInteger + { + Require(scale >= 0, "Scale must be greater than or equal to zero.", nameof(value)); + RequireIsDefined(mode, nameof(mode)); + + BigInteger integer = value.ToBigInteger(); + return scale == 0 || mode == ScaleMode.Fractional ? integer : integer * BigInteger.Pow(10, scale); + } + + /// + /// Determines whether the current value is inclusively between the specified minimum and maximum values. + /// + /// The value to check. + /// The inclusive minimum value. + /// The inclusive maximum value. + /// The underlying type. + /// Returns if the current value is inclusively between the specified minimum and maximum values; otherwise, . + public static bool IsBetween(this T value, T minimum, T maximum) where T : INumber + { + return value >= minimum && value <= maximum; + } + + /// + /// Converts the current value to a value. + /// + /// The value to convert. + /// The scale of the value. + /// The scale mode that determines how the current value should be scaled. + /// The underlying type of the value to convert. + /// Returns a representing the current value. + public static BigDecimal ToBigDecimal(this T value, int scale = default, ScaleMode mode = ScaleMode.Integral) where T : IBinaryInteger + { + return new BigDecimal(value.ToBigInteger(), scale, mode); + } + + /// + /// Converts the current value to a . + /// + /// The value to convert. + /// The mode that specifies whether the value should be converted using its binary or decimal representation. + /// Returns a new representing the current value. + public static BigDecimal ToBigDecimal(this float value, ConversionMode mode = default) + { + return new BigDecimal(value, mode); + } + + /// + /// Converts the current value to a . + /// + /// The value to convert. + /// The mode that specifies whether the value should be converted using its binary or decimal representation. + /// Returns a new representing the current value. + public static BigDecimal ToBigDecimal(this double value, ConversionMode mode = default) + { + return new BigDecimal(value, mode); + } + + /// + /// Converts the current value to a . + /// + /// The value to convert. + /// Returns a new representing the current value. + public static BigDecimal ToBigDecimal(this decimal value) + { + return new BigDecimal(value); + } + + /// + /// Converts the current value to a value. + /// + /// The value to convert. + /// The underlying type of the value to convert. + /// Returns a representing the current value. + public static BigInteger ToBigInteger(this T value) where T : IBinaryInteger + { + return BigInteger.CreateChecked(value); + } + + /// + /// Converts the current value to a . + /// + /// The value to convert. + /// The scale of the value. + /// The scale mode that determines how the current value should be scaled. + /// The underlying type of the value to convert. + /// Returns a new representing the current value. + public static decimal ToDecimal(this T value, int scale = default, ScaleMode mode = default) where T : IBinaryInteger + { + Require(scale.IsBetween(0, 28), "Scale must be between 0 and 28."); + RequireIsDefined(mode, nameof(mode)); + + BigInteger scaled = value.GetUnscaledInteger(scale, mode); + Check(scaled.IsBetween(MinDecimal, MaxDecimal), $"Value is either too large or too small to convert to {nameof(Decimal)}."); + + Int128 integer = Int128.Abs((Int128)scaled); + int lo = (int)integer; + int mid = (int)(integer >> 32); + int hi = (int)(integer >> 64); + + return new decimal(lo, mid, hi, T.IsNegative(value), (byte)scale); + } + + /// + /// Converts the current value to a value. + /// + /// The value to convert. + /// The desired scale of the specified value. + /// The scale mode that determines how the specified value should be scaled. + /// The underlying type. + /// Returns a representing the current value. + public static NumberInfo ToNumberInfo(this T value, int scale = default, ScaleMode mode = default) where T : IBinaryInteger + { + Require(scale >= 0, "Scale must be greater than or equal to zero", nameof(scale)); + RequireIsDefined(mode, nameof(mode)); + BigInteger unscaledValue = value.GetUnscaledInteger(scale, mode); + return new NumberInfo(unscaledValue, scale); + } + + /// + /// Converts the current value to a value. + /// + /// The value to convert. + /// The conversion mode that determines whether the current value should be converted from its binary or decimal representation. + /// Returns a representing the current value. + public static NumberInfo ToNumberInfo(this float value, ConversionMode mode = default) + { + RequireIsDefined(mode, nameof(mode)); + return Ieee754Converter.Convert(value, mode); + } + + /// + /// Converts the current value to a value. + /// + /// The value to convert. + /// The conversion mode that determines whether the current value should be converted from its binary or decimal representation. + /// Returns a representing the current value. + public static NumberInfo ToNumberInfo(this double value, ConversionMode mode = default) + { + RequireIsDefined(mode, nameof(mode)); + return Ieee754Converter.Convert(value, mode); + } + + /// + /// Converts the current value to a value. + /// + /// The value to convert. + /// Returns a representing the current value. + public static NumberInfo ToNumberInfo(this decimal value) + { + return new NumberInfo(value.GetUnscaledValue(), value.Scale); + } +} diff --git a/OnixLabs.Numerics/OnixLabs.Numerics.csproj b/OnixLabs.Numerics/OnixLabs.Numerics.csproj new file mode 100644 index 0000000..b6ab84c --- /dev/null +++ b/OnixLabs.Numerics/OnixLabs.Numerics.csproj @@ -0,0 +1,48 @@ + + + net8.0 + OnixLabs.Numerics + ONIXLabs + ONIXLabs Numerics API for .NET + 7.0.0 + en + enable + true + Copyright © ONIXLabs 2020 + https://github.com/onix-labs/onixlabs-dotnet + 7.0.0 + 12 + + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + embedded + true + true + true + Apache-2.0 + https://github.com/onix-labs/onixlabs-dotnet + README.md + true + true + git + https://github.com/onix-labs/onixlabs-dotnet + + + true + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/OnixLabs.Core/Text/Base58.Empty.cs b/OnixLabs.Numerics/ScaleMode.cs similarity index 59% rename from OnixLabs.Core/Text/Base58.Empty.cs rename to OnixLabs.Numerics/ScaleMode.cs index e6674c0..35cf410 100644 --- a/OnixLabs.Core/Text/Base58.Empty.cs +++ b/OnixLabs.Numerics/ScaleMode.cs @@ -1,33 +1,31 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; +namespace OnixLabs.Numerics; -namespace OnixLabs.Core.Text; - -public readonly partial struct Base58 +/// +/// Specifies decimal scale modes. +/// +public enum ScaleMode { /// - /// Gets an empty Base-58 value. + /// Specifies that unscaled values should be preserved; for example, 123 with a scale of 10 becomes 0.0000000123. /// - public static readonly Base58 Empty; + Fractional, /// - /// Initializes static members of the class. + /// Specifies that integer values should be preserved; for example, 123 with a scale of 10 becomes 123.0000000000. /// - static Base58() - { - Empty = FromByteArray(Array.Empty()); - } + Integral } diff --git a/OnixLabs.Playground/OnixLabs.Playground.csproj b/OnixLabs.Playground/OnixLabs.Playground.csproj index a5c1a76..61923dc 100644 --- a/OnixLabs.Playground/OnixLabs.Playground.csproj +++ b/OnixLabs.Playground/OnixLabs.Playground.csproj @@ -1,15 +1,20 @@ - Exe - net6.0 + net8.0 enable - 10 + 12 + true - + + false + + + + + + - - + - diff --git a/OnixLabs.Playground/Program.cs b/OnixLabs.Playground/Program.cs index c7964e3..d629839 100644 --- a/OnixLabs.Playground/Program.cs +++ b/OnixLabs.Playground/Program.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +16,7 @@ namespace OnixLabs.Playground; internal static class Program { - private static void Main(string[] args) + private static void Main() { } } diff --git a/OnixLabs.Security.Cryptography.UnitTests/KeyTestBase.cs b/OnixLabs.Security.Cryptography.UnitTests.Data/MerkleNode.cs similarity index 60% rename from OnixLabs.Security.Cryptography.UnitTests/KeyTestBase.cs rename to OnixLabs.Security.Cryptography.UnitTests.Data/MerkleNode.cs index 095071b..43e0b6a 100644 --- a/OnixLabs.Security.Cryptography.UnitTests/KeyTestBase.cs +++ b/OnixLabs.Security.Cryptography.UnitTests.Data/MerkleNode.cs @@ -1,30 +1,25 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; +using System.Security.Cryptography; -namespace OnixLabs.Security.Cryptography.UnitTests; +namespace OnixLabs.Security.Cryptography.UnitTests.Data; -public abstract class KeyTestBase +public sealed record MerkleNode(string Text, int Number, DateTime Moment, Guid Identifier) : IHashable { - protected static byte[] GenerateRandomData(int length = 1024) + public Hash ComputeHash(HashAlgorithm algorithm) { - byte[] result = new byte[length]; - Random random = new(Guid.NewGuid().GetHashCode()); - - random.NextBytes(result); - - return result; + return Hash.Compute(algorithm, ToString()); } } diff --git a/OnixLabs.Security.Cryptography.UnitTests.Data/OnixLabs.Security.Cryptography.UnitTests.Data.csproj b/OnixLabs.Security.Cryptography.UnitTests.Data/OnixLabs.Security.Cryptography.UnitTests.Data.csproj new file mode 100644 index 0000000..c69cd04 --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests.Data/OnixLabs.Security.Cryptography.UnitTests.Data.csproj @@ -0,0 +1,29 @@ + + + net8.0 + enable + enable + + false + + 12 + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/OnixLabs.Security.Cryptography.UnitTests.Data/TestPrivateKey.cs b/OnixLabs.Security.Cryptography.UnitTests.Data/TestPrivateKey.cs new file mode 100644 index 0000000..d9f0270 --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests.Data/TestPrivateKey.cs @@ -0,0 +1,24 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Security.Cryptography; + +namespace OnixLabs.Security.Cryptography.UnitTests.Data; + +public sealed class TestPrivateKey(ReadOnlySpan value) : PrivateKey(value) +{ + public override PublicKey GetPublicKey() => new TestPublicKey([]); + public override byte[] ExportPkcs8PrivateKey() => []; + public override byte[] ExportPkcs8PrivateKey(ReadOnlySpan password, PbeParameters parameters) => []; +} diff --git a/OnixLabs.Security.Cryptography.UnitTests.Data/TestPublicKey.cs b/OnixLabs.Security.Cryptography.UnitTests.Data/TestPublicKey.cs new file mode 100644 index 0000000..2e4390e --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests.Data/TestPublicKey.cs @@ -0,0 +1,17 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace OnixLabs.Security.Cryptography.UnitTests.Data; + +public sealed class TestPublicKey(ReadOnlySpan value) : PublicKey(value); diff --git a/OnixLabs.Security.Cryptography.UnitTests/DigitalSignatureTests.cs b/OnixLabs.Security.Cryptography.UnitTests/DigitalSignatureTests.cs new file mode 100644 index 0000000..497453f --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests/DigitalSignatureTests.cs @@ -0,0 +1,123 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Xunit; + +namespace OnixLabs.Security.Cryptography.UnitTests; + +public sealed class DigitalSignatureTests +{ + [Fact(DisplayName = "DigitalSignature should be constructable from bytes")] + public void DigitalSignatureShouldBeConstructableFromBytes() + { + // Given + byte[] value = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + const string expected = "000102030405060708090a0b0c0d0e0f"; + + // When + DigitalSignature candidate = new(value); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "DigitalSignature value should not be modified when altering the original byte array")] + public void DigitalSignatureValueShouldNotBeModifiedWhenAlteringTheOriginalByteArray() + { + // Given + byte[] value = [1, 2, 3, 4]; + DigitalSignature candidate = new(value); + const string expected = "01020304"; + + // When + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "DigitalSignature value should not be modified when altering the obtained byte array")] + public void DigitalSignatureValueShouldNotBeModifiedWhenAlteringTheObtainedByteArray() + { + // Given + DigitalSignature candidate = new([1, 2, 3, 4]); + const string expected = "01020304"; + + // When + byte[] value = candidate.ToByteArray(); + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Identical signature values should be considered equal")] + public void IdenticalDigitalSignatureValuesShouldBeConsideredEqual() + { + // Given + DigitalSignature left = new([1, 2, 3, 4]); + DigitalSignature right = new([1, 2, 3, 4]); + + // Then + Assert.Equal(left, right); + Assert.True(left.Equals(right)); + Assert.True(left == right); + } + + [Fact(DisplayName = "Different signature values should not be considered equal")] + public void DifferentDigitalSignatureValuesShouldNotBeConsideredEqual() + { + // Given + DigitalSignature left = new([1, 2, 3, 4]); + DigitalSignature right = new([5, 6, 7, 8]); + + // Then + Assert.NotEqual(left, right); + Assert.False(left.Equals(right)); + Assert.True(left != right); + } + + [Fact(DisplayName = "Identical signature values should produce identical hash codes")] + public void IdenticalDigitalSignatureValuesShouldProduceIdenticalDigitalSignatureCodes() + { + // Given + DigitalSignature left = new([1, 2, 3, 4]); + DigitalSignature right = new([1, 2, 3, 4]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.Equal(leftHashCode, rightHashCode); + } + + [Fact(DisplayName = "Different signature values should produce different hash codes")] + public void DifferentDigitalSignatureValuesShouldProduceDifferentDigitalSignatureCodes() + { + // Given + DigitalSignature left = new([1, 2, 3, 4]); + DigitalSignature right = new([5, 6, 7, 8]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.NotEqual(leftHashCode, rightHashCode); + } +} diff --git a/OnixLabs.Security.Cryptography.UnitTests/EcdsaEncryptedPkcs8Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/EcdsaEncryptedPkcs8Tests.cs deleted file mode 100644 index 12ffbbc..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/EcdsaEncryptedPkcs8Tests.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Collections.Generic; -using System.Security.Cryptography; -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class EcdsaKeyEncryptedPkcs8Tests : KeyTestBase -{ - [Fact(DisplayName = "Two identical ECDSA PKCS #8 private keys should be considered equal")] - public void TwoIdenticalPrivateKeysShouldBeConsideredEqual() - { - // Arrange - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - const string password = "This is a secret!"; - PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64); - - // Act - byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters); - PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type); - - // Assert - Assert.Equal(privateKey1, privateKey2); - } - - [Fact(DisplayName = "Two identical ECDSA PKCS #8 keys should be able to sign and verify the same data")] - public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameData() - { - // Arrange - IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - const string password = "This is a secret!"; - PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64); - byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters); - PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - DigitalSignature signature1 = privateKey1.SignData(data); - DigitalSignature signature2 = privateKey2.SignData(data); - - signatures.Add((signature1, data)); - signatures.Add((signature2, data)); - } - - // Assert - foreach ((DigitalSignature signature, byte[] data) in signatures) - { - Assert.True(signature.IsDataValid(data, publicKey1)); - Assert.True(signature.IsDataValid(data, publicKey2)); - } - } - - [Fact(DisplayName = "Two identical ECDSA PKCS #8 keys should be able to sign and verify the same hash")] - public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameHash() - { - // Arrange - IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - const string password = "This is a secret!"; - PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64); - byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters); - PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - Hash hashedData = Hash.ComputeSha2Hash256(data); - DigitalSignature signature1 = privateKey1.SignHash(hashedData); - DigitalSignature signature2 = privateKey2.SignHash(hashedData); - - signatures.Add((signature1, hashedData)); - signatures.Add((signature2, hashedData)); - } - - // Assert - foreach ((DigitalSignature signature, Hash hashedData) in signatures) - { - Assert.True(signature.IsHashValid(hashedData, publicKey1)); - Assert.True(signature.IsHashValid(hashedData, publicKey2)); - } - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyPkcs8Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyPkcs8Tests.cs deleted file mode 100644 index 72cb3e1..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyPkcs8Tests.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Collections.Generic; -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class EcdsaKeyPkcs8Tests : KeyTestBase -{ - [Fact(DisplayName = "Two identical ECDSA PKCS #8 private keys should be considered equal")] - public void TwoIdenticalPrivateKeysShouldBeConsideredEqual() - { - // Arrange - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - - // Act - byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(); - PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, type); - - // Assert - Assert.Equal(privateKey1, privateKey2); - } - - [Fact(DisplayName = "Two identical ECDSA PKCS #8 keys should be able to sign and verify the same data")] - public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameData() - { - // Arrange - IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(privateKey1.ExportPkcs8Key(), type); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - DigitalSignature signature1 = privateKey1.SignData(data); - DigitalSignature signature2 = privateKey2.SignData(data); - - signatures.Add((signature1, data)); - signatures.Add((signature2, data)); - } - - // Assert - foreach ((DigitalSignature signature, byte[] data) in signatures) - { - Assert.True(signature.IsDataValid(data, publicKey1)); - Assert.True(signature.IsDataValid(data, publicKey2)); - } - } - - [Fact(DisplayName = "Two identical ECDSA PKCS #8 keys should be able to sign and verify the same hash")] - public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameHash() - { - // Arrange - IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(privateKey1.ExportPkcs8Key(), type); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - Hash hashedData = Hash.ComputeSha2Hash256(data); - DigitalSignature signature1 = privateKey1.SignHash(hashedData); - DigitalSignature signature2 = privateKey2.SignHash(hashedData); - - signatures.Add((signature1, hashedData)); - signatures.Add((signature2, hashedData)); - } - - // Assert - foreach ((DigitalSignature signature, Hash hashedData) in signatures) - { - Assert.True(signature.IsHashValid(hashedData, publicKey1)); - Assert.True(signature.IsHashValid(hashedData, publicKey2)); - } - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyTests.cs b/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyTests.cs index dfdbcea..45ff4f8 100644 --- a/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyTests.cs +++ b/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyTests.cs @@ -1,117 +1,43 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Collections.Generic; -using OnixLabs.Core.Text; +using System.Security.Cryptography; using Xunit; namespace OnixLabs.Security.Cryptography.UnitTests; -public sealed class EcdsaKeyTests : KeyTestBase +public sealed class EcdsaKeyTests { - [Fact(DisplayName = "Two identical ECDSA private keys should be considered equal")] - public void TwoIdenticalPrivateKeysShouldBeConsideredEqual() + [Fact(DisplayName = "ECDSA sign and verify with two identical keys should succeed")] + public void EcdsaSignAndVerifyWithTwoIdenticalKeysShouldSucceed() { - // Arrange - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - - // Act - Base58 base58PrivateKey = privateKey1.ToBase58(); - PrivateKey privateKey2 = EcdsaPrivateKey.FromBase58(base58PrivateKey, type); - - // Assert - Assert.Equal(privateKey1, privateKey2); - } - - [Fact(DisplayName = "Two identical ECDSA public keys should be considered equal")] - public void TwoIdenticalPublicKeysShouldBeConsideredEqual() - { - // Arrange - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PublicKey publicKey1 = pair.PublicKey; - - // Act - Base58 base58PublicKey = publicKey1.ToBase58(); - PublicKey publicKey2 = EcdsaPublicKey.FromBase58(base58PublicKey, type); - - // Assert - Assert.Equal(publicKey1, publicKey2); - } - - [Fact(DisplayName = "Two identical ECDSA keys should be able to sign and verify the same data")] - public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameData() - { - // Arrange - IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - PrivateKey privateKey2 = EcdsaPrivateKey.FromBase64(privateKey1.ToBase64(), type); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - DigitalSignature signature1 = privateKey1.SignData(data); - DigitalSignature signature2 = privateKey2.SignData(data); - - signatures.Add((signature1, data)); - signatures.Add((signature2, data)); - } - - // Assert - foreach ((DigitalSignature signature, byte[] data) in signatures) - { - Assert.True(signature.IsDataValid(data, publicKey1)); - Assert.True(signature.IsDataValid(data, publicKey2)); - } - } - - [Fact(DisplayName = "Two identical ECDSA keys should be able to sign and verify the same hash")] - public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameHash() - { - // Arrange - IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - KeyPair pair = KeyPair.CreateEcdsaKeyPair(type); - PrivateKey privateKey1 = pair.PrivateKey; - PrivateKey privateKey2 = EcdsaPrivateKey.FromBase64(privateKey1.ToBase64(), type); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - Hash hashedData = Hash.ComputeSha2Hash256(data); - DigitalSignature signature1 = privateKey1.SignHash(hashedData); - DigitalSignature signature2 = privateKey2.SignHash(hashedData); - - signatures.Add((signature1, hashedData)); - signatures.Add((signature2, hashedData)); - } - - // Assert - foreach ((DigitalSignature signature, Hash hashedData) in signatures) - { - Assert.True(signature.IsHashValid(hashedData, publicKey1)); - Assert.True(signature.IsHashValid(hashedData, publicKey2)); - } + // Given + byte[] data = Salt.CreateNonZero(2048).ToByteArray(); + HashAlgorithm algorithm = SHA256.Create(); + IEcdsaPrivateKey privateKey1 = EcdsaPrivateKey.Create(); + IEcdsaPrivateKey privateKey2 = new EcdsaPrivateKey(privateKey1.ToByteArray()); + IEcdsaPublicKey publicKey1 = privateKey1.GetPublicKey(); + IEcdsaPublicKey publicKey2 = privateKey2.GetPublicKey(); + + // When + DigitalSignature signature1 = new(privateKey1.SignData(data, algorithm)); + DigitalSignature signature2 = new(privateKey2.SignData(data, algorithm)); + + // Then + Assert.True(publicKey1.IsDataValid(signature1, data, algorithm)); + Assert.True(publicKey1.IsDataValid(signature2, data, algorithm)); + Assert.True(publicKey2.IsDataValid(signature1, data, algorithm)); + Assert.True(publicKey2.IsDataValid(signature2, data, algorithm)); } } diff --git a/OnixLabs.Security.Cryptography.UnitTests/HashAlgorithmExtensionTests.cs b/OnixLabs.Security.Cryptography.UnitTests/HashAlgorithmExtensionTests.cs new file mode 100644 index 0000000..e172a98 --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests/HashAlgorithmExtensionTests.cs @@ -0,0 +1,55 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using Xunit; + +namespace OnixLabs.Security.Cryptography.UnitTests; + +public sealed class HashAlgorithmExtensionTests +{ + [Fact(DisplayName = "HashAlgorithm.ComputeHash should produce the expected result with two rounds")] + public void HashAlgorithmComputeHashShouldProduceExpectedResultWithTwoRounds() + { + // Given + using HashAlgorithm algorithm = SHA256.Create(); + const string expected = "efaaeb3b1d1d85e8587ef0527ca43b9575ce8149ba1ee41583d3d19bd130daf8"; + + // When + byte[] bytes = algorithm.ComputeHash("abc123", rounds: 2); + string actual = Convert.ToHexString(bytes).ToLower(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "HashAlgorithm.ComputeHashAsync should produce the expected result with two rounds")] + public async void HashAlgorithmComputeHashAsyncShouldProduceExpectedResultWithTwoRounds() + { + // Given + using HashAlgorithm algorithm = SHA256.Create(); + Stream data = new MemoryStream(Encoding.Default.GetBytes("abc123")); + const string expected = "efaaeb3b1d1d85e8587ef0527ca43b9575ce8149ba1ee41583d3d19bd130daf8"; + + // When + byte[] bytes = await algorithm.ComputeHashAsync(data, 2); + string actual = Convert.ToHexString(bytes).ToLower(); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Security.Cryptography.UnitTests/HashAsyncTests.cs b/OnixLabs.Security.Cryptography.UnitTests/HashAsyncTests.cs deleted file mode 100644 index ffba6fc..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/HashAsyncTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class HashAsyncTests -{ - [Fact(DisplayName = "Identical hashes should be considered equal")] - public async void IdenticalHashesShouldBeConsideredEqual() - { - // Arrange - Hash a = await Hash.ComputeSha2Hash256Async("abcdefghijklmnopqrstuvwxyz"); - Hash b = await Hash.ComputeSha2Hash256Async("abcdefghijklmnopqrstuvwxyz"); - - // Assert - Assert.Equal(a, b); - } - - [Fact(DisplayName = "Different hashes should not be considered equal")] - public async void DifferentHashesShouldNotBeConsideredEqual() - { - // Arrange - Hash a = await Hash.ComputeSha2Hash256Async("abcdefghijklmnopqrstuvwxyz"); - Hash b = await Hash.ComputeSha2Hash256Async("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - - // Assert - Assert.NotEqual(a, b); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/HashTests.cs b/OnixLabs.Security.Cryptography.UnitTests/HashTests.cs index 240c376..58dc039 100644 --- a/OnixLabs.Security.Cryptography.UnitTests/HashTests.cs +++ b/OnixLabs.Security.Cryptography.UnitTests/HashTests.cs @@ -1,86 +1,429 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.IO; +using System.Security.Cryptography; +using OnixLabs.Core; using Xunit; namespace OnixLabs.Security.Cryptography.UnitTests; public sealed class HashTests { - [Fact(DisplayName = "Identical Hash values produce identical hash codes.")] - public void IdenticalHashValuesProduceIdenticalHashCodes() + [Fact(DisplayName = "Hash should be constructable from bytes")] + public void HashShouldBeConstructableFromBytes() { - // Arrange - Hash a = Hash.ComputeSha2Hash256("abcdefghijklmnopqrstuvwxyz"); - Hash b = Hash.ComputeSha2Hash256("abcdefghijklmnopqrstuvwxyz"); + // Given + byte[] value = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + const string expected = "000102030405060708090a0b0c0d0e0f"; - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); + // When + Hash candidate = new(value); + string actual = candidate.ToString(); - // Assert - Assert.Equal(hashCodeA, hashCodeB); + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Hash should be constructable from byte and length")] + public void HashShouldBeConstructableFromByteAndLength() + { + // Given + const byte value = 0xF0; + const int length = 16; + const string expected = "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0"; + + // When + Hash candidate = new(value, length); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Hash value should not be modified when altering the original byte array")] + public void HashValueShouldNotBeModifiedWhenAlteringTheOriginalByteArray() + { + // Given + byte[] value = [1, 2, 3, 4]; + Hash candidate = new(value); + const string expected = "01020304"; + + // When + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Hash value should not be modified when altering the obtained byte array")] + public void HashValueShouldNotBeModifiedWhenAlteringTheObtainedByteArray() + { + // Given + Hash candidate = new([1, 2, 3, 4]); + const string expected = "01020304"; + + // When + byte[] value = candidate.ToByteArray(); + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Identical hash values should be considered equal")] + public void IdenticalHashValuesShouldBeConsideredEqual() + { + // Given + Hash left = new([1, 2, 3, 4]); + Hash right = new([1, 2, 3, 4]); + + // Then + Assert.Equal(left, right); + Assert.True(left.Equals(right)); + Assert.True(left == right); } - [Fact(DisplayName = "Identical hashes should be considered equal")] - public void IdenticalHashesShouldBeConsideredEqual() + [Fact(DisplayName = "Different hash values should not be considered equal")] + public void DifferentHashValuesShouldNotBeConsideredEqual() { - // Arrange - Hash a = Hash.ComputeSha2Hash256("abcdefghijklmnopqrstuvwxyz"); - Hash b = Hash.ComputeSha2Hash256("abcdefghijklmnopqrstuvwxyz"); + // Given + Hash left = new([1, 2, 3, 4]); + Hash right = new([5, 6, 7, 8]); - // Assert - Assert.Equal(a, b); + // Then + Assert.NotEqual(left, right); + Assert.False(left.Equals(right)); + Assert.True(left != right); + } + + [Fact(DisplayName = "Identical hash values should produce identical hash codes")] + public void IdenticalHashValuesShouldProduceIdenticalHashCodes() + { + // Given + Hash left = new([1, 2, 3, 4]); + Hash right = new([1, 2, 3, 4]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.Equal(leftHashCode, rightHashCode); + } + + [Fact(DisplayName = "Different hash values should produce different hash codes")] + public void DifferentHashValuesShouldProduceDifferentHashCodes() + { + // Given + Hash left = new([1, 2, 3, 4]); + Hash right = new([5, 6, 7, 8]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.NotEqual(leftHashCode, rightHashCode); + } + + [Fact(DisplayName = "Hashes should produce a negative-one sort order when the left-hand hash is lesser than the right-hand hash")] + public void HashesShouldProduceANegativeOneSortOrderWhenTheLeftHandHashIsLesserThanTheRightHandHash() + { + // Given + Hash left = new([1]); + Hash right = new([2]); + const int expected = -1; + + // When + int actual = left.CompareTo(right); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Hashes should produce a positive-one sort order when the left-hand hash is greater than the right-hand hash")] + public void HashesShouldProduceAPositiveOneSortOrderWhenTheLeftHandHashIsGreaterThanTheRightHandHash() + { + // Given + Hash left = new([2]); + Hash right = new([1]); + const int expected = 1; + + // When + int actual = left.CompareTo(right); + + // Then + Assert.Equal(expected, actual); } - [Fact(DisplayName = "Different hashes should not be considered equal")] - public void DifferentHashesShouldNotBeConsideredEqual() + [Fact(DisplayName = "Hashes should produce a zero sort order when the left-hand hash is equal to the right-hand hash")] + public void HashesShouldProduceAZeroSortOrderWhenTheLeftHandHashIsEqualToTheRightHandHash() { - // Arrange - Hash a = Hash.ComputeSha2Hash256("abcdefghijklmnopqrstuvwxyz"); - Hash b = Hash.ComputeSha2Hash256("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + // Given + Hash left = new([1]); + Hash right = new([1]); + const int expected = 0; - // Assert - Assert.NotEqual(a, b); + // When + int actual = left.CompareTo(right); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Hash.Compute should produce the expected hash using a byte array")] + [InlineData("abc123", "MD5", "e99a18c428cb38d5f260853678922e03")] + [InlineData("abc123", "SHA1", "6367c48dd193d56ea7b0baad25b19455e529f5ee")] + [InlineData("abc123", "SHA256", "6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090")] + [InlineData("abc123", "SHA384", "a31d79891919cad24f3264479d76884f581bee32e86778373db3a124de975dd86a40fc7f399b331133b281ab4b11a6ca")] + [InlineData("abc123", "SHA512", "c70b5dd9ebfb6f51d09d4132b7170c9d20750a7852f00680f65658f0310e810056e6763c34c9a00b0e940076f54495c169fc2302cceb312039271c43469507dc")] + public void HashComputeShouldProduceTheExpectedHashUsingAByteArray(string data, string algorithmName, string expected) + { + // Given + byte[] bytes = data.ToByteArray(); + HashAlgorithm algorithm = algorithmName switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA384" => SHA384.Create(), + "SHA512" => SHA512.Create(), + _ => throw new ArgumentException($"Unknown hash algorithm name: {algorithmName}.") + }; + + // When + Hash candidate = Hash.Compute(algorithm, bytes); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Hash.Compute should produce the expected hash using a byte array with two rounds")] + [InlineData("abc123", "MD5", "b106dc6352e5ec1f8aafd8c406d34d92")] + [InlineData("abc123", "SHA1", "6691484ea6b50ddde1926a220da01fa9e575c18a")] + [InlineData("abc123", "SHA256", "efaaeb3b1d1d85e8587ef0527ca43b9575ce8149ba1ee41583d3d19bd130daf8")] + [InlineData("abc123", "SHA384", "d58e9a112b8c637df5d2e33af03ce738dd1c57657243d70d2fa8f76a99fa9a0e2f4abf50d9a88e8958f2d5f6fa002190")] + [InlineData("abc123", "SHA512", "c2c9d705d7a1ed34247649bbe64c6edd2035e0a4c9ae1c063170f5ee2aeca09125cc0a8b30593c07a18801d6e0570de22e8dc40a59bc1f59a49834c05ed49949")] + public void HashComputeShouldProduceTheExpectedHashUsingAByteArrayWithTwoRounds(string data, string algorithmName, string expected) + { + // Given + byte[] bytes = data.ToByteArray(); + HashAlgorithm algorithm = algorithmName switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA384" => SHA384.Create(), + "SHA512" => SHA512.Create(), + _ => throw new ArgumentException($"Unknown hash algorithm name: {algorithmName}.") + }; + + // When + Hash candidate = Hash.Compute(algorithm, bytes, 2); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Hash.Compute should produce the expected hash using a byte array with an offset and count")] + [InlineData("abc123", 1, 3, "MD5", "b79f52be223290bd34f94e92aa8b0bdd")] + [InlineData("abc123", 1, 3, "SHA1", "be4a30dd01a93831a222262d9fa288c4f016b822")] + [InlineData("abc123", 1, 3, "SHA256", "fa54bf6e8e528001735fcc222c3ef5b99c46f469d9340deae3d9577818a6fe5a")] + [InlineData("abc123", 1, 3, "SHA384", "3cfd879e784ed23f3e9142775218bbaf636bd5413d32583a10f79f6b63028cbe9e241273dabe293c27876db2ecbaa594")] + [InlineData("abc123", 1, 3, "SHA512", "65f2fea7da1b1e470169d7f861000047ac78e00a024f5973322e5850d5fd61ceb94b7252629426bfa4beb3dafc9f55c747b5b2a8374f545e19148e61ef0057cc")] + public void HashComputeShouldProduceTheExpectedHashUsingAByteArrayWithAnOffsetAndCount(string data, int offset, int count, string algorithmName, string expected) + { + // Given + byte[] bytes = data.ToByteArray(); + HashAlgorithm algorithm = algorithmName switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA384" => SHA384.Create(), + "SHA512" => SHA512.Create(), + _ => throw new ArgumentException($"Unknown hash algorithm name: {algorithmName}.") + }; + + // When + Hash candidate = Hash.Compute(algorithm, bytes, offset, count); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Hash.Compute should produce the expected hash using a byte array with an offset, count and two rounds")] + [InlineData("abc123", 1, 3, "MD5", "05787e59f464916f6dfdf5bf5996e152")] + [InlineData("abc123", 1, 3, "SHA1", "33dcbf41c6e49b12ad37b5dab4d95dcc6ee71f49")] + [InlineData("abc123", 1, 3, "SHA256", "5a9828edeba2a57521b2d40648cc69a4e0c236111dfae612075399ba588eee91")] + [InlineData("abc123", 1, 3, "SHA384", "5691be426e2e501f5598cfc0355fc7de2c1c15637daf98ee09bf7d1da75463bf33a96a2164facbd535515c54e5d56920")] + [InlineData("abc123", 1, 3, "SHA512", "74da074abe82913bc91a3079ac7d55b8bb1e111d10647b31d6c881a93427ebc57a6c4aeca5efba612e9b71c38e6601a13df0e7d73e1530ed65453c8926404186")] + public void HashComputeShouldProduceTheExpectedHashUsingAByteArrayWithAnOffsetCountAndTwoRounds(string data, int offset, int count, string algorithmName, string expected) + { + // Given + byte[] bytes = data.ToByteArray(); + HashAlgorithm algorithm = algorithmName switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA384" => SHA384.Create(), + "SHA512" => SHA512.Create(), + _ => throw new ArgumentException($"Unknown hash algorithm name: {algorithmName}.") + }; + + // When + Hash candidate = Hash.Compute(algorithm, bytes, offset, count, 2); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Hash.Compute should produce the expected hash using a stream")] + [InlineData("abc123", "MD5", "e99a18c428cb38d5f260853678922e03")] + [InlineData("abc123", "SHA1", "6367c48dd193d56ea7b0baad25b19455e529f5ee")] + [InlineData("abc123", "SHA256", "6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090")] + [InlineData("abc123", "SHA384", "a31d79891919cad24f3264479d76884f581bee32e86778373db3a124de975dd86a40fc7f399b331133b281ab4b11a6ca")] + [InlineData("abc123", "SHA512", "c70b5dd9ebfb6f51d09d4132b7170c9d20750a7852f00680f65658f0310e810056e6763c34c9a00b0e940076f54495c169fc2302cceb312039271c43469507dc")] + public void HashComputeShouldProduceTheExpectedHashUsingAStream(string data, string algorithmName, string expected) + { + // Given + Stream stream = new MemoryStream(data.ToByteArray()); + HashAlgorithm algorithm = algorithmName switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA384" => SHA384.Create(), + "SHA512" => SHA512.Create(), + _ => throw new ArgumentException($"Unknown hash algorithm name: {algorithmName}.") + }; + + // When + Hash candidate = Hash.Compute(algorithm, stream); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Hash.Compute should produce the expected hash using a stream and two rounds")] + [InlineData("abc123", "MD5", "b106dc6352e5ec1f8aafd8c406d34d92")] + [InlineData("abc123", "SHA1", "6691484ea6b50ddde1926a220da01fa9e575c18a")] + [InlineData("abc123", "SHA256", "efaaeb3b1d1d85e8587ef0527ca43b9575ce8149ba1ee41583d3d19bd130daf8")] + [InlineData("abc123", "SHA384", "d58e9a112b8c637df5d2e33af03ce738dd1c57657243d70d2fa8f76a99fa9a0e2f4abf50d9a88e8958f2d5f6fa002190")] + [InlineData("abc123", "SHA512", "c2c9d705d7a1ed34247649bbe64c6edd2035e0a4c9ae1c063170f5ee2aeca09125cc0a8b30593c07a18801d6e0570de22e8dc40a59bc1f59a49834c05ed49949")] + public void HashComputeShouldProduceTheExpectedHashUsingAStreamAndTwoRounds(string data, string algorithmName, string expected) + { + // Given + Stream stream = new MemoryStream(data.ToByteArray()); + HashAlgorithm algorithm = algorithmName switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA384" => SHA384.Create(), + "SHA512" => SHA512.Create(), + _ => throw new ArgumentException($"Unknown hash algorithm name: {algorithmName}.") + }; + + // When + Hash candidate = Hash.Compute(algorithm, stream, 2); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Hash.ComputeAsync should produce the expected hash using a stream")] + [InlineData("abc123", "MD5", "e99a18c428cb38d5f260853678922e03")] + [InlineData("abc123", "SHA1", "6367c48dd193d56ea7b0baad25b19455e529f5ee")] + [InlineData("abc123", "SHA256", "6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090")] + [InlineData("abc123", "SHA384", "a31d79891919cad24f3264479d76884f581bee32e86778373db3a124de975dd86a40fc7f399b331133b281ab4b11a6ca")] + [InlineData("abc123", "SHA512", "c70b5dd9ebfb6f51d09d4132b7170c9d20750a7852f00680f65658f0310e810056e6763c34c9a00b0e940076f54495c169fc2302cceb312039271c43469507dc")] + public async void HashComputeAsyncShouldProduceTheExpectedHashUsingAStream(string data, string algorithmName, string expected) + { + // Given + Stream stream = new MemoryStream(data.ToByteArray()); + HashAlgorithm algorithm = algorithmName switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA384" => SHA384.Create(), + "SHA512" => SHA512.Create(), + _ => throw new ArgumentException($"Unknown hash algorithm name: {algorithmName}.") + }; + + // When + Hash candidate = await Hash.ComputeAsync(algorithm, stream); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); } - [Fact(DisplayName = "Parse should be able to parse a known hash")] - public void ParseShouldBeAbleToParseAKnownHash() + [Theory(DisplayName = "Hash.ComputeAsync should produce the expected hash using a stream and two rounds")] + [InlineData("abc123", "MD5", "b106dc6352e5ec1f8aafd8c406d34d92")] + [InlineData("abc123", "SHA1", "6691484ea6b50ddde1926a220da01fa9e575c18a")] + [InlineData("abc123", "SHA256", "efaaeb3b1d1d85e8587ef0527ca43b9575ce8149ba1ee41583d3d19bd130daf8")] + [InlineData("abc123", "SHA384", "d58e9a112b8c637df5d2e33af03ce738dd1c57657243d70d2fa8f76a99fa9a0e2f4abf50d9a88e8958f2d5f6fa002190")] + [InlineData("abc123", "SHA512", "c2c9d705d7a1ed34247649bbe64c6edd2035e0a4c9ae1c063170f5ee2aeca09125cc0a8b30593c07a18801d6e0570de22e8dc40a59bc1f59a49834c05ed49949")] + public async void HashComputeAsyncShouldProduceTheExpectedHashUsingAStreamAndTwoRounds(string data, string algorithmName, string expected) { - // Arrange - const string expected = "Sha2Hash256:dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"; + // Given + Stream stream = new MemoryStream(data.ToByteArray()); + HashAlgorithm algorithm = algorithmName switch + { + "MD5" => MD5.Create(), + "SHA1" => SHA1.Create(), + "SHA256" => SHA256.Create(), + "SHA384" => SHA384.Create(), + "SHA512" => SHA512.Create(), + _ => throw new ArgumentException($"Unknown hash algorithm name: {algorithmName}.") + }; - // Act - Hash hash = Hash.Parse(expected); - string actual = hash.ToStringWithAlgorithmType(); + // When + Hash candidate = await Hash.ComputeAsync(algorithm, stream, 2); + string actual = candidate.ToString(); - // Assert + // Then Assert.Equal(expected, actual); } - [Fact(DisplayName = "Parse should be able to parse an unknown hash")] - public void ParseShouldBeAbleToParseAnUnknownHash() + [Fact(DisplayName = "Hash.Concatenate should produce the expected result")] + public void HashConcatenateShouldProduceExpectedResult() { - // Arrange - const string value = "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"; - const string expected = "Unknown:dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"; + // Given + using HashAlgorithm algorithm = SHA256.Create(); + Hash left = Hash.Compute(algorithm, "abc"); + Hash right = Hash.Compute(algorithm, "123"); + const string expected = "c1702b0f46b4a0d3ef1e19feef861727cc583a157594e264571126818c820592"; - // Act - Hash hash = Hash.Parse(value); - string actual = hash.ToStringWithAlgorithmType(); + // When + Hash concatenated = left.Concatenate(algorithm, right); + string actual = concatenated.ToString(); - // Assert + // Then Assert.Equal(expected, actual); } } diff --git a/OnixLabs.Security.Cryptography.UnitTests/HmacAsyncTests.cs b/OnixLabs.Security.Cryptography.UnitTests/HmacAsyncTests.cs deleted file mode 100644 index 12d7382..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/HmacAsyncTests.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class HmacAsyncTests -{ - [Fact(DisplayName = "Identical HMACs should be considered equal")] - public async void IdenticalHashesShouldBeConsideredEqual() - { - // Arrange - Hmac a = await Hmac.ComputeSha2Hmac256Async("abcdefghijklmnopqrstuvwxyz", "key"); - Hmac b = await Hmac.ComputeSha2Hmac256Async("abcdefghijklmnopqrstuvwxyz", "key"); - - // Assert - Assert.Equal(a, b); - } - - [Fact(DisplayName = "Different HMACs should not be considered equal (different data)")] - public async void DifferentHashesShouldNotBeConsideredEqualWithDifferentData() - { - // Arrange - Hmac a = await Hmac.ComputeSha2Hmac256Async("abcdefghijklmnopqrstuvwxyz", "key"); - Hmac b = await Hmac.ComputeSha2Hmac256Async("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "key"); - - // Assert - Assert.NotEqual(a, b); - } - - [Fact(DisplayName = "Different HMACs should not be considered equal (different keys)")] - public async void DifferentHashesShouldNotBeConsideredEqualWithDifferentKeys() - { - // Arrange - Hmac a = await Hmac.ComputeSha2Hmac256Async("abcdefghijklmnopqrstuvwxyz", "key"); - Hmac b = await Hmac.ComputeSha2Hmac256Async("abcdefghijklmnopqrstuvwxyz", "123"); - - // Assert - Assert.NotEqual(a, b); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/HmacTests.cs b/OnixLabs.Security.Cryptography.UnitTests/HmacTests.cs deleted file mode 100644 index eea32c7..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/HmacTests.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class HmacTests -{ - [Fact(DisplayName = "Identical Hmac values produce identical hash codes.")] - public void IdenticalHmacValuesProduceIdenticalHashCodes() - { - // Arrange - Hmac a = Hmac.ComputeSha2Hmac256("abcdefghijklmnopqrstuvwxyz", "key"); - Hmac b = Hmac.ComputeSha2Hmac256("abcdefghijklmnopqrstuvwxyz", "key"); - - // Act - int hashCodeA = a.GetHashCode(); - int hashCodeB = b.GetHashCode(); - - // Assert - Assert.Equal(hashCodeA, hashCodeB); - } - - [Fact(DisplayName = "Identical HMACs should be considered equal")] - public void IdenticalHashesShouldBeConsideredEqual() - { - // Arrange - Hmac a = Hmac.ComputeSha2Hmac256("abcdefghijklmnopqrstuvwxyz", "key"); - Hmac b = Hmac.ComputeSha2Hmac256("abcdefghijklmnopqrstuvwxyz", "key"); - - // Assert - Assert.Equal(a, b); - } - - [Fact(DisplayName = "Different HMACs should not be considered equal (different data)")] - public void DifferentHashesShouldNotBeConsideredEqualWithDifferentData() - { - // Arrange - Hmac a = Hmac.ComputeSha2Hmac256("abcdefghijklmnopqrstuvwxyz", "key"); - Hmac b = Hmac.ComputeSha2Hmac256("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "key"); - - // Assert - Assert.NotEqual(a, b); - } - - [Fact(DisplayName = "Different HMACs should not be considered equal (different keys)")] - public void DifferentHashesShouldNotBeConsideredEqualWithDifferentKeys() - { - // Arrange - Hmac a = Hmac.ComputeSha2Hmac256("abcdefghijklmnopqrstuvwxyz", "key"); - Hmac b = Hmac.ComputeSha2Hmac256("abcdefghijklmnopqrstuvwxyz", "123"); - - // Assert - Assert.NotEqual(a, b); - } - - [Fact(DisplayName = "Parse should be able to parse a known hash")] - public void ParseShouldBeAbleToParseAKnownHash() - { - // Arrange - const string expected = - "Sha2Hmac256:73ac6fa8599f4bde8dfee594c7f5f6ff03023b2d99ca71a7eccf729a8fc5c324:48656c6c6f2c20576f726c6421"; - - // Act - Hmac hash = Hmac.Parse(expected); - string actual = hash.ToStringWithAlgorithmType(); - - // Assert - Assert.Equal(expected, actual); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeGenericTests.cs b/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeGenericTests.cs new file mode 100644 index 0000000..4714f0e --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeGenericTests.cs @@ -0,0 +1,110 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using OnixLabs.Core; +using OnixLabs.Security.Cryptography.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Security.Cryptography.UnitTests; + +public sealed class MerkleTreeGenericTests +{ + private static readonly HashAlgorithm Algorithm = SHA256.Create(); + + private readonly IEnumerable setA = + [ + new MerkleNode("abc", 123, "1953-05-08T01:00:59Z".ToDateTime(), Guid.Parse("18d1e14c-9762-4b2c-8774-3f053e8579e0")), + new MerkleNode("def", 456, "1953-05-08T06:30:00Z".ToDateTime(), Guid.Parse("a5d0ad36-f9a6-4fb0-9374-aa658eb19a51")), + new MerkleNode("hij", 789, "1953-05-08T12:00:33Z".ToDateTime(), Guid.Parse("0d324d48-d451-416e-ac4d-fed1a324d446")), + new MerkleNode("klm", 101, "1953-05-08T18:30:00Z".ToDateTime(), Guid.Parse("dc2bb378-114e-4f5d-8674-92729a67fe3d")), + new MerkleNode("nop", 112, "1953-05-08T23:00:59Z".ToDateTime(), Guid.Parse("193863c7-3bfa-4a3c-874a-a99d98b38358")) + ]; + + private readonly IEnumerable setB = + [ + new MerkleNode("qrs", 123, "1900-05-08T01:00:59Z".ToDateTime(), Guid.Parse("893576c9-7cb1-4653-a7c4-21e5ba9e7275")), + new MerkleNode("tuv", 456, "1901-05-08T06:30:00Z".ToDateTime(), Guid.Parse("e7543a7e-3a52-48f5-842e-9448931f1cde")), + new MerkleNode("qxy", 789, "1902-05-08T12:00:33Z".ToDateTime(), Guid.Parse("d228ab75-d36a-4392-9c51-888a5b7f9db7")), + new MerkleNode("zab", 101, "1903-05-08T18:30:00Z".ToDateTime(), Guid.Parse("f73180c0-1fc8-4492-ae5d-f0b8962177a8")) + ]; + + [Fact(DisplayName = "Identical Merkle trees should be considered equal")] + public void IdenticalMerkleTreesShouldBeConsideredEqual() + { + // Given / When + MerkleTree a = MerkleTree.Create(setA, Algorithm); + MerkleTree b = MerkleTree.Create(setA, Algorithm); + + // Then + Assert.Equal(a, b); + Assert.Equal(a.Hash, b.Hash); + } + + [Fact(DisplayName = "Different Merkle trees should not be considered equal")] + public void DifferentMerkleTreesShouldNotBeConsideredEqual() + { + // Given / When + MerkleTree a = MerkleTree.Create(setA, Algorithm); + MerkleTree b = MerkleTree.Create(setB, Algorithm); + + // Then + Assert.NotEqual(a, b); + Assert.NotEqual(a.Hash, b.Hash); + } + + [Fact(DisplayName = "MerkleTree.GetLeafHashes should produce the same leaf hashes that the tree was constructed with")] + public void MerkleTreeGetLeafHashesShouldProduceTheSameLeafHashesThatTheTreeWasConstructedWith() + { + // Given + IEnumerable expected = setA.Select(value => value.ComputeHash(Algorithm)); + MerkleTree candidate = MerkleTree.Create(setA, Algorithm); + + // When + IEnumerable actual = candidate.GetLeafHashes(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "MerkleTree.GetLeafValues should produce the same leaf values that the tree was constructed with")] + public void MerkleTreeGetLeafValuesShouldProduceTheSameLeafValuesThatTheTreeWasConstructedWith() + { + // Given + MerkleTree candidate = MerkleTree.Create(setA, Algorithm); + + // When + IEnumerable actual = candidate.GetLeafValues(); + + // Then + Assert.Equal(setA, actual); + } + + [Fact(DisplayName = "MerkleTree.ToMerkleTree should produce a hash-only, non-generic Merkle tree that is equal in value")] + public void MerkleTreeToMerkleTreeShouldProduceAHashOnlyNonGenericMerkleTreeThatIsEqualInValue() + { + // Given + MerkleTree a = MerkleTree.Create(setA, Algorithm); + + // When + MerkleTree b = a.ToMerkleTree(Algorithm); + + // Then + Assert.Equal(a, b); + Assert.Equal(a.Hash, b.Hash); + } +} diff --git a/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeTests.cs b/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeTests.cs index 524645d..8a29552 100644 --- a/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeTests.cs +++ b/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeTests.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,48 +14,70 @@ using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography; using Xunit; namespace OnixLabs.Security.Cryptography.UnitTests; public sealed class MerkleTreeTests { + private static readonly HashAlgorithm Algorithm = SHA256.Create(); + + private readonly IEnumerable setA = Enumerable.Range(123, 1357).Select(i => Hash.Compute(Algorithm, $"A{i}")).ToList(); + private readonly IEnumerable setB = Enumerable.Range(100, 1000).Select(i => Hash.Compute(Algorithm, $"B{i}")).ToList(); + [Fact(DisplayName = "Identical Merkle trees should be considered equal")] public void IdenticalMerkleTreesShouldBeConsideredEqual() { - // Arrange - List hashes = Enumerable - .Range(1, 937) - .Select(i => Hash.ComputeSha2Hash256(i.ToString())) - .ToList(); - - // Act - MerkleTree a = MerkleTree.Build(hashes); - MerkleTree b = MerkleTree.Build(hashes); + // Given / When + MerkleTree a = MerkleTree.Create(setA, Algorithm); + MerkleTree b = MerkleTree.Create(setA, Algorithm); - // Assert + // Then Assert.Equal(a, b); + Assert.Equal(a.Hash, b.Hash); } [Fact(DisplayName = "Different Merkle trees should not be considered equal")] public void DifferentMerkleTreesShouldNotBeConsideredEqual() { - // Arrange - List hashesForMerkleTreeA = Enumerable - .Range(1, 937) - .Select(i => Hash.ComputeSha2Hash256($"A{i}")) - .ToList(); + // Given / When + MerkleTree a = MerkleTree.Create(setA, Algorithm); + MerkleTree b = MerkleTree.Create(setB, Algorithm); + + // Then + Assert.NotEqual(a, b); + Assert.NotEqual(a.Hash, b.Hash); + } - List hashesForMerkleTreeB = Enumerable - .Range(1, 677) - .Select(i => Hash.ComputeSha2Hash256($"B{i}")) + [Fact(DisplayName = "MerkleTree.GetLeafHashes should produce the same leaf hashes that the tree was constructed with")] + public void MerkleTreeGetLeafHashesShouldProduceTheSameLeafHashesThatTheTreeWasConstructedWith() + { + // Given + MerkleTree candidate = MerkleTree.Create(setA, Algorithm); + + // When + IEnumerable actual = candidate.GetLeafHashes(); + + // Then + Assert.Equal(setA, actual); + } + + [Fact(DisplayName = "MerkleTree.GetLeafHashes should obtain all leaf hashes from a Merkle tree constructed with 1 million hashes")] + public void MerkleTreeGetLeafHashesShouldObtainAllLeafHashesFromAMerkleTreeConstructedWith1MillionHashes() + { + // Given + IEnumerable expected = Enumerable + .Range(0, 1_000_000) + .Select(value => Hash.Compute(Algorithm, value.ToString())) .ToList(); - // Act - MerkleTree a = MerkleTree.Build(hashesForMerkleTreeA); - MerkleTree b = MerkleTree.Build(hashesForMerkleTreeB); + MerkleTree tree = MerkleTree.Create(expected, Algorithm); - // Assert - Assert.NotEqual(a, b); + // When + IEnumerable actual = tree.GetLeafHashes(); + + // Then + Assert.Equal(expected, actual); } } diff --git a/OnixLabs.Security.Cryptography.UnitTests/OnixLabs.Security.Cryptography.UnitTests.csproj b/OnixLabs.Security.Cryptography.UnitTests/OnixLabs.Security.Cryptography.UnitTests.csproj index 76b5a6a..39b888f 100644 --- a/OnixLabs.Security.Cryptography.UnitTests/OnixLabs.Security.Cryptography.UnitTests.csproj +++ b/OnixLabs.Security.Cryptography.UnitTests/OnixLabs.Security.Cryptography.UnitTests.csproj @@ -1,28 +1,26 @@ - - net6.0 - + net8.0 false - - 10 + 12 - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + + + + - diff --git a/OnixLabs.Security.Cryptography.UnitTests/PrivateKeyTests.cs b/OnixLabs.Security.Cryptography.UnitTests/PrivateKeyTests.cs new file mode 100644 index 0000000..be25240 --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests/PrivateKeyTests.cs @@ -0,0 +1,124 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Security.Cryptography.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Security.Cryptography.UnitTests; + +public sealed class PrivateKeyTests +{ + [Fact(DisplayName = "PrivateKey should be constructable from bytes")] + public void PrivateKeyShouldBeConstructableFromBytes() + { + // Given + byte[] value = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + const string expected = "000102030405060708090a0b0c0d0e0f"; + + // When + PrivateKey candidate = new TestPrivateKey(value); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "PrivateKey value should not be modified when altering the original byte array")] + public void PrivateKeyValueShouldNotBeModifiedWhenAlteringTheOriginalByteArray() + { + // Given + byte[] value = [1, 2, 3, 4]; + PrivateKey candidate = new TestPrivateKey(value); + const string expected = "01020304"; + + // When + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "PrivateKey value should not be modified when altering the obtained byte array")] + public void PrivateKeyValueShouldNotBeModifiedWhenAlteringTheObtainedByteArray() + { + // Given + PrivateKey candidate = new TestPrivateKey([1, 2, 3, 4]); + const string expected = "01020304"; + + // When + byte[] value = candidate.ToByteArray(); + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Identical public key values should be considered equal")] + public void IdenticalPrivateKeyValuesShouldBeConsideredEqual() + { + // Given + PrivateKey left = new TestPrivateKey([1, 2, 3, 4]); + PrivateKey right = new TestPrivateKey([1, 2, 3, 4]); + + // Then + Assert.Equal(left, right); + Assert.True(left.Equals(right)); + Assert.True(left == right); + } + + [Fact(DisplayName = "Different public key values should not be considered equal")] + public void DifferentPrivateKeyValuesShouldNotBeConsideredEqual() + { + // Given + PrivateKey left = new TestPrivateKey([1, 2, 3, 4]); + PrivateKey right = new TestPrivateKey([5, 6, 7, 8]); + + // Then + Assert.NotEqual(left, right); + Assert.False(left.Equals(right)); + Assert.True(left != right); + } + + [Fact(DisplayName = "Identical public key values should produce identical hash codes")] + public void IdenticalPrivateKeyValuesShouldProduceIdenticalPrivateKeyCodes() + { + // Given + PrivateKey left = new TestPrivateKey([1, 2, 3, 4]); + PrivateKey right = new TestPrivateKey([1, 2, 3, 4]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.Equal(leftHashCode, rightHashCode); + } + + [Fact(DisplayName = "Different public key values should produce different hash codes")] + public void DifferentPrivateKeyValuesShouldProduceDifferentPrivateKeyCodes() + { + // Given + PrivateKey left = new TestPrivateKey([1, 2, 3, 4]); + PrivateKey right = new TestPrivateKey([5, 6, 7, 8]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.NotEqual(leftHashCode, rightHashCode); + } +} diff --git a/OnixLabs.Security.Cryptography.UnitTests/PublicKeyTests.cs b/OnixLabs.Security.Cryptography.UnitTests/PublicKeyTests.cs new file mode 100644 index 0000000..162f1b1 --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests/PublicKeyTests.cs @@ -0,0 +1,124 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Security.Cryptography.UnitTests.Data; +using Xunit; + +namespace OnixLabs.Security.Cryptography.UnitTests; + +public sealed class PublicKeyTests +{ + [Fact(DisplayName = "PublicKey should be constructable from bytes")] + public void PublicKeyShouldBeConstructableFromBytes() + { + // Given + byte[] value = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + const string expected = "000102030405060708090a0b0c0d0e0f"; + + // When + PublicKey candidate = new TestPublicKey(value); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "PublicKey value should not be modified when altering the original byte array")] + public void PublicKeyValueShouldNotBeModifiedWhenAlteringTheOriginalByteArray() + { + // Given + byte[] value = [1, 2, 3, 4]; + PublicKey candidate = new TestPublicKey(value); + const string expected = "01020304"; + + // When + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "PublicKey value should not be modified when altering the obtained byte array")] + public void PublicKeyValueShouldNotBeModifiedWhenAlteringTheObtainedByteArray() + { + // Given + PublicKey candidate = new TestPublicKey([1, 2, 3, 4]); + const string expected = "01020304"; + + // When + byte[] value = candidate.ToByteArray(); + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Identical public key values should be considered equal")] + public void IdenticalPublicKeyValuesShouldBeConsideredEqual() + { + // Given + PublicKey left = new TestPublicKey([1, 2, 3, 4]); + PublicKey right = new TestPublicKey([1, 2, 3, 4]); + + // Then + Assert.Equal(left, right); + Assert.True(left.Equals(right)); + Assert.True(left == right); + } + + [Fact(DisplayName = "Different public key values should not be considered equal")] + public void DifferentPublicKeyValuesShouldNotBeConsideredEqual() + { + // Given + PublicKey left = new TestPublicKey([1, 2, 3, 4]); + PublicKey right = new TestPublicKey([5, 6, 7, 8]); + + // Then + Assert.NotEqual(left, right); + Assert.False(left.Equals(right)); + Assert.True(left != right); + } + + [Fact(DisplayName = "Identical public key values should produce identical hash codes")] + public void IdenticalPublicKeyValuesShouldProduceIdenticalPublicKeyCodes() + { + // Given + PublicKey left = new TestPublicKey([1, 2, 3, 4]); + PublicKey right = new TestPublicKey([1, 2, 3, 4]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.Equal(leftHashCode, rightHashCode); + } + + [Fact(DisplayName = "Different public key values should produce different hash codes")] + public void DifferentPublicKeyValuesShouldProduceDifferentPublicKeyCodes() + { + // Given + PublicKey left = new TestPublicKey([1, 2, 3, 4]); + PublicKey right = new TestPublicKey([5, 6, 7, 8]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.NotEqual(leftHashCode, rightHashCode); + } +} diff --git a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyEncryptedPkcs8Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyEncryptedPkcs8Tests.cs deleted file mode 100644 index 4d30fff..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyEncryptedPkcs8Tests.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Collections.Generic; -using System.Security.Cryptography; -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class RsaKeyEncryptedPkcs8Tests : KeyTestBase -{ - [Fact(DisplayName = "Two identical RSA PKCS #8 private keys should be considered equal")] - public void TwoIdenticalPrivateKeysShouldBeConsideredEqual() - { - // Arrange - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - const string password = "This is a secret!"; - PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64); - - // Act - byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters); - PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type, padding); - - // Assert - Assert.Equal(privateKey1, privateKey2); - } - - [Fact(DisplayName = "Two identical RSA PKCS #8 keys should be able to sign and verify the same data")] - public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameData() - { - // Arrange - IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - const string password = "This is a secret!"; - PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64); - byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters); - PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type, padding); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - DigitalSignature signature1 = privateKey1.SignData(data); - DigitalSignature signature2 = privateKey2.SignData(data); - - signatures.Add((signature1, data)); - signatures.Add((signature2, data)); - } - - // Assert - foreach ((DigitalSignature signature, byte[] data) in signatures) - { - Assert.True(signature.IsDataValid(data, publicKey1)); - Assert.True(signature.IsDataValid(data, publicKey2)); - } - } - - [Fact(DisplayName = "Two identical RSA PKCS #8 keys should be able to sign and verify the same hash")] - public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameHash() - { - // Arrange - IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - const string password = "This is a secret!"; - PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64); - byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters); - PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type, padding); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - Hash hashedData = Hash.ComputeSha2Hash256(data); - DigitalSignature signature1 = privateKey1.SignHash(hashedData); - DigitalSignature signature2 = privateKey2.SignHash(hashedData); - - signatures.Add((signature1, hashedData)); - signatures.Add((signature2, hashedData)); - } - - // Assert - foreach ((DigitalSignature signature, Hash hashedData) in signatures) - { - Assert.True(signature.IsHashValid(hashedData, publicKey1)); - Assert.True(signature.IsHashValid(hashedData, publicKey2)); - } - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyPkcs8Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyPkcs8Tests.cs deleted file mode 100644 index 7e9088a..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyPkcs8Tests.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Collections.Generic; -using System.Security.Cryptography; -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class RsaKeyPkcs8Tests : KeyTestBase -{ - [Fact(DisplayName = "Two identical RSA PKCS #8 private keys should be considered equal")] - public void TwoIdenticalPrivateKeysShouldBeConsideredEqual() - { - // Arrange - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - - // Act - byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(); - PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, type, padding); - - // Assert - Assert.Equal(privateKey1, privateKey2); - } - - [Fact(DisplayName = "Two identical RSA PKCS #8 keys should be able to sign and verify the same data")] - public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameData() - { - // Arrange - IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(privateKey1.ExportPkcs8Key(), type, padding); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - DigitalSignature signature1 = privateKey1.SignData(data); - DigitalSignature signature2 = privateKey2.SignData(data); - - signatures.Add((signature1, data)); - signatures.Add((signature2, data)); - } - - // Assert - foreach ((DigitalSignature signature, byte[] data) in signatures) - { - Assert.True(signature.IsDataValid(data, publicKey1)); - Assert.True(signature.IsDataValid(data, publicKey2)); - } - } - - [Fact(DisplayName = "Two identical RSA PKCS #8 keys should be able to sign and verify the same hash")] - public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameHash() - { - // Arrange - IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(privateKey1.ExportPkcs8Key(), type, padding); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - Hash hashedData = Hash.ComputeSha2Hash256(data); - DigitalSignature signature1 = privateKey1.SignHash(hashedData); - DigitalSignature signature2 = privateKey2.SignHash(hashedData); - - signatures.Add((signature1, hashedData)); - signatures.Add((signature2, hashedData)); - } - - // Assert - foreach ((DigitalSignature signature, Hash hashedData) in signatures) - { - Assert.True(signature.IsHashValid(hashedData, publicKey1)); - Assert.True(signature.IsHashValid(hashedData, publicKey2)); - } - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyTests.cs b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyTests.cs index 91b4d64..5954ab2 100644 --- a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyTests.cs +++ b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyTests.cs @@ -1,122 +1,44 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Collections.Generic; using System.Security.Cryptography; -using OnixLabs.Core.Text; using Xunit; namespace OnixLabs.Security.Cryptography.UnitTests; -public sealed class RsaKeyTests : KeyTestBase +public sealed class RsaKeyTests { - [Fact(DisplayName = "Two identical RSA private keys should be considered equal")] - public void TwoIdenticalPrivateKeysShouldBeConsideredEqual() + [Fact(DisplayName = "RSA sign and verify with two identical keys should succeed")] + public void RsaSignAndVerifyWithTwoIdenticalKeysShouldSucceed() { - // Arrange - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - - // Act - Base58 base58PrivateKey = privateKey1.ToBase58(); - PrivateKey privateKey2 = RsaPrivateKey.FromBase58(base58PrivateKey, type, padding); - - // Assert - Assert.Equal(privateKey1, privateKey2); - } - - [Fact(DisplayName = "Two identical RSA public keys should be considered equal")] - public void TwoIdenticalPublicKeysShouldBeConsideredEqual() - { - // Arrange - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PublicKey publicKey1 = pair.PublicKey; - - // Act - Base58 base58PublicKey = publicKey1.ToBase58(); - PublicKey publicKey2 = RsaPublicKey.FromBase58(base58PublicKey, type, padding); - - // Assert - Assert.Equal(publicKey1, publicKey2); - } - - [Fact(DisplayName = "Two identical RSA keys should be able to sign and verify the same data")] - public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameData() - { - // Arrange - IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - PrivateKey privateKey2 = RsaPrivateKey.FromBase64(privateKey1.ToBase64(), type, padding); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - DigitalSignature signature1 = privateKey1.SignData(data); - DigitalSignature signature2 = privateKey2.SignData(data); - - signatures.Add((signature1, data)); - signatures.Add((signature2, data)); - } - - // Assert - foreach ((DigitalSignature signature, byte[] data) in signatures) - { - Assert.True(signature.IsDataValid(data, publicKey1)); - Assert.True(signature.IsDataValid(data, publicKey2)); - } - } - - [Fact(DisplayName = "Two identical RSA keys should be able to sign and verify the same hash")] - public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameHash() - { - // Arrange - IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>(); - HashAlgorithmType type = HashAlgorithmType.Sha2Hash256; - RSASignaturePadding padding = RSASignaturePadding.Pss; - KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding); - PrivateKey privateKey1 = pair.PrivateKey; - PrivateKey privateKey2 = RsaPrivateKey.FromBase64(privateKey1.ToBase64(), type, padding); - PublicKey publicKey1 = pair.PublicKey; - PublicKey publicKey2 = privateKey1.GetPublicKey(); - - // Act - for (int index = 0; index < 5; index++) - { - byte[] data = GenerateRandomData(); - Hash hashedData = Hash.ComputeSha2Hash256(data); - DigitalSignature signature1 = privateKey1.SignHash(hashedData); - DigitalSignature signature2 = privateKey2.SignHash(hashedData); - - signatures.Add((signature1, hashedData)); - signatures.Add((signature2, hashedData)); - } - - // Assert - foreach ((DigitalSignature signature, Hash hashedData) in signatures) - { - Assert.True(signature.IsHashValid(hashedData, publicKey1)); - Assert.True(signature.IsHashValid(hashedData, publicKey2)); - } + // Given + byte[] data = Salt.CreateNonZero(2048).ToByteArray(); + HashAlgorithmName algorithm = HashAlgorithmName.SHA256; + RSASignaturePadding padding = RSASignaturePadding.Pkcs1; + IRsaPrivateKey privateKey1 = RsaPrivateKey.Create(); + IRsaPrivateKey privateKey2 = new RsaPrivateKey(privateKey1.ToByteArray()); + IRsaPublicKey publicKey1 = privateKey1.GetPublicKey(); + IRsaPublicKey publicKey2 = privateKey2.GetPublicKey(); + + // When + DigitalSignature signature1 = new(privateKey1.SignData(data, algorithm, padding)); + DigitalSignature signature2 = new(privateKey2.SignData(data, algorithm, padding)); + + // Then + Assert.True(publicKey1.IsDataValid(signature1, data, algorithm, padding)); + Assert.True(publicKey1.IsDataValid(signature2, data, algorithm, padding)); + Assert.True(publicKey2.IsDataValid(signature1, data, algorithm, padding)); + Assert.True(publicKey2.IsDataValid(signature2, data, algorithm, padding)); } } diff --git a/OnixLabs.Security.Cryptography.UnitTests/SaltTests.cs b/OnixLabs.Security.Cryptography.UnitTests/SaltTests.cs new file mode 100644 index 0000000..f1b57ef --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests/SaltTests.cs @@ -0,0 +1,147 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Core.Linq; +using Xunit; + +namespace OnixLabs.Security.Cryptography.UnitTests; + +public sealed class SaltTests +{ + [Fact(DisplayName = "Salt should be constructable from bytes")] + public void SaltShouldBeConstructableFromBytes() + { + // Given + byte[] value = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + const string expected = "000102030405060708090a0b0c0d0e0f"; + + // When + Salt candidate = new(value); + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Salt value should not be modified when altering the original byte array")] + public void SaltValueShouldNotBeModifiedWhenAlteringTheOriginalByteArray() + { + // Given + byte[] value = [1, 2, 3, 4]; + Salt candidate = new(value); + const string expected = "01020304"; + + // When + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Salt value should not be modified when altering the obtained byte array")] + public void SaltValueShouldNotBeModifiedWhenAlteringTheObtainedByteArray() + { + // Given + Salt candidate = new([1, 2, 3, 4]); + const string expected = "01020304"; + + // When + byte[] value = candidate.ToByteArray(); + value[0] = 0; + string actual = candidate.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Fact(DisplayName = "Identical salt values should be considered equal")] + public void IdenticalSaltValuesShouldBeConsideredEqual() + { + // Given + Salt left = new([1, 2, 3, 4]); + Salt right = new([1, 2, 3, 4]); + + // Then + Assert.Equal(left, right); + Assert.True(left.Equals(right)); + Assert.True(left == right); + } + + [Fact(DisplayName = "Different salt values should not be considered equal")] + public void DifferentSaltValuesShouldNotBeConsideredEqual() + { + // Given + Salt left = new([1, 2, 3, 4]); + Salt right = new([5, 6, 7, 8]); + + // Then + Assert.NotEqual(left, right); + Assert.False(left.Equals(right)); + Assert.True(left != right); + } + + [Fact(DisplayName = "Identical salt values should produce identical hash codes")] + public void IdenticalSaltValuesShouldProduceIdenticalSaltCodes() + { + // Given + Salt left = new([1, 2, 3, 4]); + Salt right = new([1, 2, 3, 4]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.Equal(leftHashCode, rightHashCode); + } + + [Fact(DisplayName = "Different salt values should produce different hash codes")] + public void DifferentSaltValuesShouldProduceDifferentSaltCodes() + { + // Given + Salt left = new([1, 2, 3, 4]); + Salt right = new([5, 6, 7, 8]); + + // When + int leftHashCode = left.GetHashCode(); + int rightHashCode = right.GetHashCode(); + + // Then + Assert.NotEqual(leftHashCode, rightHashCode); + } + + [Fact(DisplayName = "Salt.Create should produce a salt of the specified length")] + public void SaltCreateShouldProduceASaltOfTheSpecifiedLength() + { + // Given / When + const int expected = 32; + Salt candidate = Salt.Create(expected); + + // Then + Assert.Equal(expected, candidate.ToByteArray().Length); + } + + [Fact(DisplayName = "Salt.CreateNonZero should produce a salt of the specified length of non-zero bytes")] + public void SaltCreateNonZeroShouldProduceASaltOfTheSpecifiedLengthOfNonZeroBytes() + { + // Given / When + const int expected = 32; + Salt candidate = Salt.CreateNonZero(expected); + + // Then + Assert.Equal(expected, candidate.ToByteArray().Length); + Assert.True(candidate.ToByteArray().None(value => value is 0)); + } +} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash224Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash224Tests.cs deleted file mode 100644 index 1de9700..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash224Tests.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class Sha3Hash224Tests : Sha3HashTestBase -{ - protected override Sha3 HashAlgorithm => Sha3.CreateSha3Hash224(); - - [Theory(DisplayName = "Sha3Managed224 should produce the expected hash for the specified string literal")] - [InlineData("6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "")] - [InlineData("9877af03f5e1919851d0ef4ce6b23f1e85a40b446d93713f4c6e6dcd", "1234567890")] - [InlineData("beae76edd99d4ad4d398d51c5ea1d8b7b3fa6d49d687b0cb1ec2ec41", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("5cdeca81e123f87cad96b9cba999f16f6d41549608d4e0f4681b8239", "abcdefghijklmnopqrstuvwxyz")] - public override void TestSha3WithLiteralString(string expected, string literal) - { - base.TestSha3WithLiteralString(expected, literal); - } - - [Theory(DisplayName = "Sha3Managed224 should produce the expected hash for the specified string template")] - [InlineData("d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c", "a", 1_000_000)] - public override void TestSha3WithGeneratedString(string expected, string template, int iterations) - { - base.TestSha3WithGeneratedString(expected, template, iterations); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash256Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash256Tests.cs deleted file mode 100644 index e355c65..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash256Tests.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class Sha3Hash256Tests : Sha3HashTestBase -{ - protected override Sha3 HashAlgorithm => Sha3.CreateSha3Hash256(); - - [Theory(DisplayName = "Sha3Managed256 should produce the expected hash for the specified string literal")] - [InlineData("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", "")] - [InlineData("01da8843e976913aa5c15a62d45f1c9267391dcbd0a76ad411919043f374a163", "1234567890")] - [InlineData("738eeb2d4adf0d452456695011bb252bd4701a0ae78fdd3fc945a963bceb1702", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData("7cab2dc765e21b241dbc1c255ce620b29f527c6d5e7f5f843e56288f0d707521", "abcdefghijklmnopqrstuvwxyz")] - public override void TestSha3WithLiteralString(string expected, string literal) - { - base.TestSha3WithLiteralString(expected, literal); - } - - [Theory(DisplayName = "Sha3Managed256 should produce a valid hash result for the given string template")] - [InlineData("5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1", "a", 1_000_000)] - public override void TestSha3WithGeneratedString(string expected, string template, int iterations) - { - base.TestSha3WithGeneratedString(expected, template, iterations); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash384Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash384Tests.cs deleted file mode 100644 index 5124b0f..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash384Tests.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class Sha3Managed384Tests : Sha3HashTestBase -{ - protected override Sha3 HashAlgorithm => Sha3.CreateSha3Hash384(); - - [Theory(DisplayName = "Sha3Managed384 should produce the expected hash for the specified string literal")] - [InlineData( - "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004", - "" - )] - [InlineData( - "6fdddab7d670f202629531c1a51b32ca30696d0af4dd5b0fbb5f82c0aba5e505110455f37d7ef73950c2bb0495a38f56", - "1234567890" - )] - [InlineData( - "284da0df47fc9e75a4ef1248f69ca0d12a5d44508942e63b03b8c227510c2e1b43400009fcd36c0acc941679e5024a04", - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - )] - [InlineData( - "fed399d2217aaf4c717ad0c5102c15589e1c990cc2b9a5029056a7f7485888d6ab65db2370077a5cadb53fc9280d278f", - "abcdefghijklmnopqrstuvwxyz" - )] - public override void TestSha3WithLiteralString(string expected, string literal) - { - base.TestSha3WithLiteralString(expected, literal); - } - - [Theory(DisplayName = "Sha3Managed384 should produce a valid hash result for the given string template")] - [InlineData("eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340", - "a", 1_000_000)] - public override void TestSha3WithGeneratedString(string expected, string template, int iterations) - { - base.TestSha3WithGeneratedString(expected, template, iterations); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash512Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash512Tests.cs deleted file mode 100644 index 3160f2e..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash512Tests.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class Sha3Managed512Tests : Sha3HashTestBase -{ - protected override Sha3 HashAlgorithm => Sha3.CreateSha3Hash512(); - - [Theory(DisplayName = "Sha3Managed512 should produce the expected hash for the specified message")] - [InlineData( - "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", - "")] - [InlineData( - "36dde7d288a2166a651d51ec6ded9e70e72cf6b366293d6f513c75393c57d6f33b949879b9d5e7f7c21cd8c02ede75e74fc54ea15bd043b4df008533fc68ae69", - "1234567890")] - [InlineData( - "69958b041bc72e9922e02cd4250953ee69d5f6e69f97d8def72b34effc0aea2bf5cfe03bd4ada0e271060593395656c1bf9eb68d1fc4cf146f90601152222df7", - "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] - [InlineData( - "af328d17fa28753a3c9f5cb72e376b90440b96f0289e5703b729324a975ab384eda565fc92aaded143669900d761861687acdc0a5ffa358bd0571aaad80aca68", - "abcdefghijklmnopqrstuvwxyz")] - public override void TestSha3WithLiteralString(string expected, string literal) - { - base.TestSha3WithLiteralString(expected, literal); - } - - [Theory(DisplayName = "Sha3Managed512 should produce the expected hash for the specified string template")] - [InlineData( - "3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87", - "a", 1_000_000)] - public override void TestSha3WithGeneratedString(string expected, string template, int iterations) - { - base.TestSha3WithGeneratedString(expected, template, iterations); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3HashTestBase.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3HashTestBase.cs deleted file mode 100644 index 75c363b..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/Sha3HashTestBase.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public abstract class Sha3HashTestBase -{ - protected abstract Sha3 HashAlgorithm { get; } - - public virtual void TestSha3WithLiteralString(string expected, string literal) - { - string actual = ComputeHash(HashAlgorithm, literal); - Assert.Equal(expected, actual); - } - - public virtual void TestSha3WithGeneratedString(string expected, string template, int iterations) - { - string actual = ComputeHash(HashAlgorithm, string.Concat(Enumerable.Repeat(template, iterations))); - Assert.Equal(expected, actual); - } - - private static string ComputeHash(HashAlgorithm algorithm, string plainText) - { - byte[] plainTextBytes = Encoding.Default.GetBytes(plainText); - byte[] hashedBytes = algorithm.ComputeHash(plainTextBytes); - return Hash.FromByteArray(hashedBytes).ToString(); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake128Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake128Tests.cs deleted file mode 100644 index 2849e64..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake128Tests.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class Sha3ManagedShake128Tests : Sha3ShakeTestBase -{ - [Theory(DisplayName = "Sha3ManagedShake128 should produce the expected hash for the specified string literal")] - [InlineData("7f9c2ba4", "", 4)] - [InlineData("7f9c2ba4e88f827d", "", 8)] - [InlineData("7f9c2ba4e88f827d616045507605853e", "", 16)] - [InlineData("7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26", "", 32)] - [InlineData("1cd2c71a", "1234567890", 4)] - [InlineData("1cd2c71a52e3f2a6", "1234567890", 8)] - [InlineData("1cd2c71a52e3f2a620173e915f17648d", "1234567890", 16)] - [InlineData("1cd2c71a52e3f2a620173e915f17648dcc43443ef78754302c6b44cf47daf527", "1234567890", 32)] - [InlineData("5b3b6a58", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4)] - [InlineData("5b3b6a587417f8fa", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 8)] - [InlineData("5b3b6a587417f8fa192aba21c16b7b7a", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 16)] - [InlineData("5b3b6a587417f8fa192aba21c16b7b7a6375aac5f04e950f", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 24)] - [InlineData("961c919c", "abcdefghijklmnopqrstuvwxyz", 4)] - [InlineData("961c919c0854576e", "abcdefghijklmnopqrstuvwxyz", 8)] - [InlineData("961c919c0854576e561320e81514bf37", "abcdefghijklmnopqrstuvwxyz", 16)] - [InlineData("961c919c0854576e561320e81514bf3724197d0715e16a36", "abcdefghijklmnopqrstuvwxyz", 24)] - public override void TestSha3WithLiteralString(string expected, string literal, int length) - { - HashAlgorithm = Sha3.CreateSha3Shake128(length); - base.TestSha3WithLiteralString(expected, literal, length); - } - - [Theory(DisplayName = "Sha3ManagedShake128 should produce the expected hash for the specified string template")] - [InlineData("9d222c79", "a", 1_000_000, 4)] - [InlineData("9d222c79c4ff9d09", "a", 1_000_000, 8)] - [InlineData("9d222c79c4ff9d092cf6ca86143aa411", "a", 1_000_000, 16)] - [InlineData("9d222c79c4ff9d092cf6ca86143aa411e369973808ef97093255826c5572ef58", "a", 1_000_000, 32)] - public override void TestSha3WithGeneratedString(string expected, string template, int iterations, int length) - { - HashAlgorithm = Sha3.CreateSha3Shake128(length); - base.TestSha3WithGeneratedString(expected, template, iterations, length); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake256Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake256Tests.cs deleted file mode 100644 index d36885c..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake256Tests.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public sealed class Sha3ManagedShake256Tests : Sha3ShakeTestBase -{ - [Theory(DisplayName = "Sha3ManagedShake256 should produce the expected hash for the specified string literal")] - [InlineData("46b9dd2b", "", 4)] - [InlineData("46b9dd2b0ba88d13", "", 8)] - [InlineData("46b9dd2b0ba88d13233b3feb743eeb24", "", 16)] - [InlineData("46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", "", 32)] - [InlineData("cd65a4e5", "1234567890", 4)] - [InlineData("cd65a4e553405b50", "1234567890", 8)] - [InlineData("cd65a4e553405b50c2f37001ea81905f", "1234567890", 16)] - [InlineData("cd65a4e553405b50c2f37001ea81905f36d650cc775fdad898b2e343644cb3db", "1234567890", 32)] - [InlineData("fa8775b6", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4)] - [InlineData("fa8775b64bf3aaf1", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 8)] - [InlineData("fa8775b64bf3aaf10d7f473c460f4d23", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 16)] - [InlineData("fa8775b64bf3aaf10d7f473c460f4d2361f56ff34ae7267a", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 24)] - [InlineData("b7b78b04", "abcdefghijklmnopqrstuvwxyz", 4)] - [InlineData("b7b78b04a3dd30a2", "abcdefghijklmnopqrstuvwxyz", 8)] - [InlineData("b7b78b04a3dd30a265c8886c33fda947", "abcdefghijklmnopqrstuvwxyz", 16)] - [InlineData("b7b78b04a3dd30a265c8886c33fda94799853de5d3d10541", "abcdefghijklmnopqrstuvwxyz", 24)] - public override void TestSha3WithLiteralString(string expected, string literal, int length) - { - HashAlgorithm = Sha3.CreateSha3Shake256(length); - base.TestSha3WithLiteralString(expected, literal, length); - } - - [Theory(DisplayName = "Sha3ManagedShake256 should produce the expected hash for the specified string template")] - [InlineData("3578a7a4", "a", 1_000_000, 4)] - [InlineData("3578a7a4ca913756", "a", 1_000_000, 8)] - [InlineData("3578a7a4ca9137569cdf76ed617d31bb", "a", 1_000_000, 16)] - [InlineData("3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd13a", "a", 1_000_000, 32)] - public override void TestSha3WithGeneratedString(string expected, string template, int iterations, int length) - { - HashAlgorithm = Sha3.CreateSha3Shake256(length); - base.TestSha3WithGeneratedString(expected, template, iterations, length); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3ShakeTestBase.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3ShakeTestBase.cs deleted file mode 100644 index 92f9d71..0000000 --- a/OnixLabs.Security.Cryptography.UnitTests/Sha3ShakeTestBase.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using Xunit; - -namespace OnixLabs.Security.Cryptography.UnitTests; - -public abstract class Sha3ShakeTestBase -{ - protected Sha3 HashAlgorithm { get; set; } - - public virtual void TestSha3WithLiteralString(string expected, string literal, int length) - { - string actual = ComputeHash(HashAlgorithm, literal); - Assert.Equal(expected, actual); - } - - public virtual void TestSha3WithGeneratedString(string expected, string template, int iterations, int length) - { - string actual = ComputeHash(HashAlgorithm, string.Concat(Enumerable.Repeat(template, iterations))); - Assert.Equal(expected, actual); - } - - private static string ComputeHash(HashAlgorithm algorithm, string plainText) - { - byte[] plainTextBytes = Encoding.Default.GetBytes(plainText); - byte[] hashedBytes = algorithm.ComputeHash(plainTextBytes); - return Hash.FromByteArray(hashedBytes).ToString(); - } -} diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Tests.cs new file mode 100644 index 0000000..3e22151 --- /dev/null +++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3Tests.cs @@ -0,0 +1,601 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Security.Cryptography; +using Xunit; + +namespace OnixLabs.Security.Cryptography.UnitTests; + +public sealed class Sha3Tests +{ + [Theory(DisplayName = "Sha3Hash224 should produce the expected result")] + [InlineData('A', "97e2f98c0938943ab1a18a1721a04dff922ecc1ad14d4bbf905c02ca")] + [InlineData('B', "b60bd459170afa28b3ef45a22ce41ede9ad62a9a0b250482a7e1beb6")] + [InlineData('C', "64d7aef1a5121b17a819ed6a6da3ac4930420a916f30acfe909f9482")] + [InlineData('D', "3cc1b4bd4dcd8ecf49d86d4970fa0e3af4a6266f35c2244d8b19a360")] + [InlineData('E', "61886c21370ba76d87ef2ef10ee1f508dbb1f7eeb239786568ac8aa9")] + [InlineData('F', "1e1e62312963bebff717b3b45a47003e0aa77815aee43368b45c2002")] + [InlineData('G', "9fe797ef1f52cf86c139f14aba3beb34225f96e0f3712ee5648fa120")] + [InlineData('H', "2aebfcaa105dea01662466ab8d82e304c374328e622ffaa0e796ae82")] + [InlineData('I', "9439a92d09b67af52e62a43318f9fb5f5f56ff8c5561d66a484966f0")] + [InlineData('J', "0b79dc2770e78755e9f0ec888f7ab92cee97938d19b6aa066c6e6d45")] + [InlineData('K', "5ea1f4dccfd9cf83485c4766180f0b7dbe4cc7bc78435d5b749fc0da")] + [InlineData('L', "90efae0893370cb53c261a30b69fd026d4b1c5c3221bc9a568ac19e3")] + [InlineData('M', "fe908de89314cebfe8e5b5d10e1b800b0e8fc033ef08dccc0d9c295e")] + [InlineData('N', "9c65eb6b181a665acf87a19cafedaff7d48f8b66b50b171a283c9370")] + [InlineData('O', "69a9eed164711b1201e4d3c6db567a3417805ef140d7c2bb168a3ffa")] + [InlineData('P', "49e17197a1d79e6dc7fac1cb5e1a44f08ee151ec3e01188b57842243")] + [InlineData('Q', "8f01734a973963b99c731ddf95c7d64d162674aa4382727a56854cdb")] + [InlineData('R', "2a2d0471bb24c160c7f839b3652f01e292f15aa09278daa1253c88f9")] + [InlineData('S', "aa50f8575a9e0791313375fd0232300a7aea9dd1fcc20c6de991aea0")] + [InlineData('T', "b7cd747c390db6bb6847225755a4aa0f7eeb66363452c45284556dbb")] + [InlineData('U', "35e0a16a4a92f63cab78df6587ec8f227d995516c893d7a966db62a4")] + [InlineData('V', "a0393621904f8a7f08f8f949428af418b609f7fab8e126175d7489be")] + [InlineData('W', "e7e8c042c1b9889070330ee25e12a09a5a01c309308a2322a414b878")] + [InlineData('X', "7b6fc9c4054463d97c8c90dd89049b67bb8f148952c2070eeda1bb86")] + [InlineData('Y', "d738effb07d0b422f4ccc99a822b32fbc926f6ba3d052cca4b1f93c2")] + [InlineData('Z', "f41ad234fb099cc6b05cce263862fe3e06bded3d097671bc8f68ad5f")] + [InlineData('a', "9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b")] + [InlineData('b', "8ec94b5ae7bae885e5b1fdeaa6fc2ca2af27febfdb7cdfaa6745fd52")] + [InlineData('c', "d77fb1a25346cfaecd2b491c3a3b452f10cda5a5b37842f2bcbfa5b2")] + [InlineData('d', "af81fd2b118fc4b3ed11bd42e7c056de57e29fcde0b0f236adaa4e25")] + [InlineData('e', "6e22f57104bb4b8dea42a97f517f12e9e28d667c97f2ca0c29d55f8a")] + [InlineData('f', "3a9de008f526d239d89905e2203fa484f6e68dfc096a7c051eb80f15")] + [InlineData('g', "84342e0cfccf62d91ef86c2698b3d0294152b8f908bf95fc0273c00d")] + [InlineData('h', "aea0b0e10eac300d286f13946455240a5978a4e4748ea87062f8a45a")] + [InlineData('i', "ca0e1bce24fbaf261d98d7492adac27652a5f9e2ba06f3c0199437bc")] + [InlineData('j', "7689b617e73b395ecfeb897ec177fbe5bf29307c8975dfbf5602ea22")] + [InlineData('k', "4c483a7e33059a013a845d18195bbaf47b0398c4ae117d16255c737e")] + [InlineData('l', "35fd6f346c0b2fa77a32a1c8d961d08450da1cd6494afca64b57955f")] + [InlineData('m', "c3835c6f733db2d1f556e5b2f32f11ad59ad0a7e996a25376951cd81")] + [InlineData('n', "3250c7b05b5f3af6db4e62ac1a75f0697aeb63921007eb81f24e691b")] + [InlineData('o', "25ab1ac565856d499f958012c0842e432050c3988037c7919021f011")] + [InlineData('p', "4295784315214500e4b51a46b3db88b139a811763aa705c5449f962d")] + [InlineData('q', "774057ce10e1b255cfa747982782e969231ef434a057622021ff5b9c")] + [InlineData('r', "d0b8f1b118091fbc0486a81919e2d7171a6310d4892130f33c51aa1d")] + [InlineData('s', "001ad0de4e72638f77b9fa1272d3aa26ac5458b7b3d6f37f1dc1319a")] + [InlineData('t', "bc3858492b7530bf2adafa0ddc6773863e41b8af69cc6da336d32691")] + [InlineData('u', "74d73ebda3f089ec06c9d2e6fbf49a88f8b72d67a98f617d2e19c34d")] + [InlineData('v', "4a7afb90ea8bf1a5c91d22700b7ebfaea729857df2d4797e0acde861")] + [InlineData('w', "297c52529a495674b68a3c8cc129bcdacc123b65e63a54d3e94a44a7")] + [InlineData('x', "63e6ceb28ad474fa51c3d5dda2239adb5e58a1ae2600d18c6e116746")] + [InlineData('y', "5849850aa0264269112e0d8d25f1336e6caf4b9dab34ae092b01f608")] + [InlineData('z', "3360e4f1a619f1628be65abec0da2992d4c7ceab1d2d3671ef4790b7")] + [InlineData('0', "a823c3f51659da24d9a61254e9f61c39a4c8f11fd65820542403dd1c")] + [InlineData('1', "300d01f3a910045fefa16d6a149f38167b2503dbc37c1b24fd6f751e")] + [InlineData('2', "f3ff4f073ed24d62051c8d7bb73418b95db2f6ff9e4441af466f6d98")] + [InlineData('3', "b6f194539618d1e5eec08a56b8c7d09b8198fe1faa3f16e9703b91bd")] + [InlineData('4', "51bbf7daffa13cd37d2517dd38b1be95b200053bd4e36492b5566bda")] + [InlineData('5', "4a63debd3538267188df39677b980ddf64ff563264554210b43524ea")] + [InlineData('6', "970a4c0b7081bd6a245822c7e804d704db34d32acc7b771208a8c24a")] + [InlineData('7', "aa98ecf6824dad085b259424c29f535bc339c94bace0a7a031ec40e6")] + [InlineData('8', "2531506e5f2f02bd42cbbd39b3f8a181e120eb662b5c85471fa913b3")] + [InlineData('9', "2299019d2c50f7525fc39a05256f802de6e9d05328c903d298b03d9d")] + public void Sha3Hash224ShouldProduceExpectedResult(char character, string expected) + { + // Given + using HashAlgorithm algorithm = Sha3.CreateSha3Hash224(); + + // When + Hash hash = Hash.Compute(algorithm, [character]); + string actual = hash.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Sha3Hash226 should produce the expected result")] + [InlineData('A', "1c9ebd6caf02840a5b2b7f0fc870ec1db154886ae9fe621b822b14fd0bf513d6")] + [InlineData('B', "521ec18851e17bbba961bc46c70baf03ee67ebdea11a8306de39c15a90e9d2e5")] + [InlineData('C', "2248e6be26f60c9baa59adbda2a136a4a5305d7b475d8465ba4911b4886e39a5")] + [InlineData('D', "037f4095baddc6f37fde4740c304b1691512d2fc9cf7ede8a93b8c9ec3d1fe07")] + [InlineData('E', "e63a84c18447bfca5c67b20a58fc6a4fefa762e4fa0e6b3b2e46f64daba345e5")] + [InlineData('F', "caf04597f01603582b91c53d5dad9c6c481445b5160a976a44c35ed428b439d7")] + [InlineData('G', "25c69eebe727567130b3e3320395e3ec854138e6ea5034dc79eebcbeb86da200")] + [InlineData('H', "2cee232f0cf383960ac375e090a647e2afd4ebeb12b4cecb1bf91c2e0f4b3408")] + [InlineData('I', "c837f30e97185c362830b324e58a3e6782095ee8457109b27f03819ff516e121")] + [InlineData('J', "5445d3d5a46c7a99219705ea5b6daa7f870b83e721da6f252f2304b94a6d7d05")] + [InlineData('K', "078773c4efc5ce946952e92d25f89d0cdd1603fc21842df386fa55855613e8cb")] + [InlineData('L', "266ddf27cb4fb4223962ef29090bcc4f50e363bca75c581756f605fb91f9c7e8")] + [InlineData('M', "6920014bef534e7eea89545a50d6aef0921f1972efcddce9f22f04a45b47d472")] + [InlineData('N', "345baaa13bbe3a40695db7697fbe3f64206323b77cf3635902106f9f29667361")] + [InlineData('O', "60c4004508ddcd8d1b0ea1c56ed1e5679d756d72e40f1a00820dbe5d9f69ff63")] + [InlineData('P', "e5a73514ffed2f2f59b5112f4ae50cb138f1658633d354ac36c7c1bc019259d2")] + [InlineData('Q', "ba86a2a6dac23e336a34b4337eb740d40d900fae703bf55dcde8430208bb82e8")] + [InlineData('R', "d034b2b544e4ffb619a9c156ae578fe21f38eb0997f097ca9569807ca157f4f6")] + [InlineData('S', "164a93c6619015a4ed2d50a49c0d98252296e3e4c7fa5277656188edb3fe71b7")] + [InlineData('T', "b3291957374e0a836351d5129cf45a5e0f73a92edff7b2c85ef159062301829e")] + [InlineData('U', "78fe1396dda648dcbccc3c17af4cd29de873f2cdf5e4c5eb04e0ef08e86cc267")] + [InlineData('V', "3eecb4a5c11c8bab18ddad1d268c827aaabb17c83f51869832a5af15efdedfcb")] + [InlineData('W', "4cea338a15eccf7f51d8297c2873b1c5d0e5bea7d52eb7e984500b0759937d0d")] + [InlineData('X', "31660a8aa8b0991f2d115272fecba9f9fe21e0798377c2b965405039319a1452")] + [InlineData('Y', "08ad231c95c5b60ab9757d6f95672f4e8731910a8f4573a90a1798ee8127ee94")] + [InlineData('Z', "1fb80b3947f9fa50760bc627a0341d53715fb79013184b34f4c0a306b62fdf05")] + [InlineData('a', "80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b")] + [InlineData('b', "b039179a8a4ce2c252aa6f2f25798251c19b75fc1508d9d511a191e0487d64a7")] + [InlineData('c', "263ab762270d3b73d3e2cddf9acc893bb6bd41110347e5d5e4bd1d3c128ea90a")] + [InlineData('d', "4ce8765e720c576f6f5a34ca380b3de5f0912e6e3cc5355542c363891e54594b")] + [InlineData('e', "42538602949f370aa331d2c07a1ee7ff26caac9cc676288f94b82eb2188b8465")] + [InlineData('f', "a0b37b8bfae8e71330bd8e278e4a45ca916d00475dd8b85e9352533454c9fec8")] + [InlineData('g', "9f2898da52dedaca29f05bcac0c8e43e4b9f7cb5707c14cc3f35a567232cec7c")] + [InlineData('h', "5a082c81a7e4d5833ee20bd67d2f4d736f679da33e4bebd3838217cb27bec1d3")] + [InlineData('i', "bf872d20c4ef70ab19c9d413f172ce399a30ddeca771658561b1443111069c9e")] + [InlineData('j', "f35e560e05de779f2669b9f513c2a7ab81dfeb100e2f4ee1fb17354bfa2740ca")] + [InlineData('k', "7c712596135d13a73c0dd366151b9440f3e9072371b436371107f12b3d850180")] + [InlineData('l', "3e5e3e723953551a2ba2e7c5584bcc4ce407414af1ab2569051e7c9bfa33164d")] + [InlineData('m', "1b42f48aa4371867a7c51ae6f237f35626e02c12eefa592614e1b10af7769370")] + [InlineData('n', "8ee93ceda95bbe450f7fb53a700c56dfac4387e48eb127881a2a68727bc7810c")] + [InlineData('o', "12c6debe02a118f89049700e723650d269838a76024a826607b163bc2a237031")] + [InlineData('p', "14c68e20d8ddb4dbd248ed14bdb2012cfcee23530af0f71328009d1e90bb36ac")] + [InlineData('q', "8a5e1d339fafc39350fd8cf1d7ca7982091c27f6b77f75bd4ddab3df425b4f8c")] + [InlineData('r', "f695d5fe6e2c67fe29ccf09341c29ad58154c568c5917a919c31936a3c96d607")] + [InlineData('s', "cdc56a5028e51232cb28194fb1eb93e7014d60fb7afb447a49a1e1aaa640c9a4")] + [InlineData('t', "889729e8d2d8864a59db1e195ad67c76949578ff2b4637388564a81dd68fc01e")] + [InlineData('u', "d7e9468290673221249673d2b82c3cb316819a8496c2f2dba3eaebd9477af44c")] + [InlineData('v', "453c8391bbd41309b79d7acc1382c2b0fb5f6b67f686d77c410666336ff9dabb")] + [InlineData('w', "f1cfdca558ac0c00464ca0f3e265ec6fb32c57caeb106fbfed9f174f6b814642")] + [InlineData('x', "741efa311f97686956946758e0d95f70f11ff2da4f2feb7c54314f44134ac49f")] + [InlineData('y', "9d0f3db671f9fb22104b984763616732d383154a7a0dcdbb9ec17ab647b64961")] + [InlineData('z', "3b4aed1c401f71809c93e713f4b86fb6d56c5b668f4ad8b474cb8884756aac46")] + [InlineData('0', "f9e2eaaa42d9fe9e558a9b8ef1bf366f190aacaa83bad2641ee106e9041096e4")] + [InlineData('1', "67b176705b46206614219f47a05aee7ae6a3edbe850bbbe214c536b989aea4d2")] + [InlineData('2', "b1b1bd1ed240b1496c81ccf19ceccf2af6fd24fac10ae42023628abbe2687310")] + [InlineData('3', "1bf0b26eb2090599dd68cbb42c86a674cb07ab7adc103ad3ccdf521bb79056b9")] + [InlineData('4', "b410677b84ed73fac43fcf1abd933151dd417d932a0ef9b0260ecf8b7b72ecb9")] + [InlineData('5', "86bc56fc56af4c3cde021282f6b727ee9f90dd636e0b0c712a85d416c75e652d")] + [InlineData('6', "0c67354981e9068905680b57898ad4f04b993c63eb66aa3f19cdfdc71d88077e")] + [InlineData('7', "8f9b51ce624f01b0a40c9f68ba8bb0a2c06aa7f95d1ed27d6b1b5e1e99ee5e4d")] + [InlineData('8', "d14a329a1924592faf2d4ba6dc727d59af6afae983a0c208bf980237b63a5a6a")] + [InlineData('9', "7609430974b087595488c154bf5c079887ead0e8efd4055cd136fda96a5ccbf8")] + public void Sha3Hash256ShouldProduceExpectedResult(char character, string expected) + { + // Given + using HashAlgorithm algorithm = Sha3.CreateSha3Hash256(); + + // When + Hash hash = Hash.Compute(algorithm, [character]); + string actual = hash.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Sha3Hash384 should produce the expected result")] + [InlineData('A', "15000d20f59aa483b5eac0a1f33abe8e09dea1054d173d3e7443c68035b99240b50f7abdb9553baf220320384c6b1cd6")] + [InlineData('B', "8283d235852af9bbf7d81037b8b70aaba733a4433a4438f1b944c04c9e1d9d6d927e96d61b1fb7e7ecfcf2983ad816b5")] + [InlineData('C', "7b95dc4d4d327449e6b84bd769d053f190568504270b32789990ac7b75bef7fcfda2931d16cb5785b9ae61a15bbead55")] + [InlineData('D', "3ff600ef369ae191ec740e0119926f7e7b7621dbc0f4de818045b88e659230969894e1af894a4edd7a97812f5d20cc10")] + [InlineData('E', "8d479cadb1a83bd1864a636b1cefd7ac06f7c96940f022ac7732336e96a2cb27198153d314842182b79c063c4ae8f4a3")] + [InlineData('F', "01c126a5c70fa68dc33d3be695070b4d837e83caa3b76df066283361f4b942abd40510747f449c4e0d19861dc0a4d413")] + [InlineData('G', "224f9604e3b38a03e335b0a78f6710f36f4ad12e2c58289caba9ddac124bd0076c912fe02a787d4cfe1f29ff4ddb75b8")] + [InlineData('H', "1ebe9a1bc7ef3153626b1e4726aa6e28800ba39cec42491bb3b526f9b825a7ba7e22e24beaa4ab1b55b6e6a44520d1ba")] + [InlineData('I', "daf032833345d6ead75be31f8c713590530b5a1a6d7134cdcb8dfcf4eedb67fa124d6cfea0d2a02f68f986062b12dc5c")] + [InlineData('J', "4f6dace508a646b071eb60546aabe666829a8cf50a8d3c8e97888a2a1db5b12b8991e517e2331983f0fc99b6e0a92431")] + [InlineData('K', "5c61420db106b2cf478a04948ea59114c1a994c594fc923afacc04858715d69126f0089515872042dfbe6a8d5e062e4b")] + [InlineData('L', "cd3967f516705f3abbff77e35c8cfcfc6a8f915df60cc5a56ec51248580deedd6d043c364923285932ac00bc85fa6705")] + [InlineData('M', "55b0a1bb027524210861d27a5554e4c24d7b48da7373c98d398a760edc2a9c62ffa4d2b15c792ea93b5576ba2624e376")] + [InlineData('N', "c8894d0f096c9232ff7545833642c20061d6e93c75d558c020fdd92ff278336757ab14be364b2bfb306c1af4bd42a312")] + [InlineData('O', "3f0cb1cfa5db5e4741efa72c8c309f3669efe7ac6619a7528b5ff4f22da37403e9e68bce96aabd7b67cf387b548e608b")] + [InlineData('P', "04ac39f4be45e26b203fc712e1072801cf0f90dec53a26e1be1fb886340e6cb06375464f7150b58adcad215dccce732c")] + [InlineData('Q', "c66a74deabfeab86cdfd9fe16d8e20915087ae3b3627113bac0bd739822c07a609b45b29bca82501a6dc4aefe09def41")] + [InlineData('R', "3bd4e7dcad9d3c02adfa7aa5388727d346278a9a7b007f497b48a4fa2a12b9545c820df150854a8f8c494275bd6fd941")] + [InlineData('S', "bcc4f6d7426e832ccbdd03e30f6dc6b837c5f5780f8efd58122ab9086edb78d86b985e6b4dbd9f89d24486431dbc89be")] + [InlineData('T', "daf2c5c49a615b7ca9a92c85800f690788246809a23652f7b613fb231065cdf110a872dfc3e094b47be4a986e9a451e5")] + [InlineData('U', "351d1a799be78278a04f2e9128d992ff130251a9b88ab26a5525097c10e1209e60a2fbfc379a0ea983a46dac261ff469")] + [InlineData('V', "08e8ee1a3416fbcb0ca7d0d53556090c17f966de3a7dda5db0267aeb9088fa5486f9a425111c4c9d7a4227118ac4faf4")] + [InlineData('W', "653fac6cdf0fad1c202f71847e9360d18b0ab62b4e0279226eba96f70631747b3853624cf8160dbad575642563fec65b")] + [InlineData('X', "7b02671ba315c6c59b8ed4d1fa0aff47ce1c46b6ae534e9f3083d03bcd8af85f730a835daa1608da913478dcaae4caa3")] + [InlineData('Y', "17b15a22de43482d1a07c944812703b1b147928d23e8a4e9c117e8d7e6aa3132b65d50836eeb64969030bd150dea4d77")] + [InlineData('Z', "8bceb69fabf2bf9ffed6f132c037301870fcfc2de42fcb01df1f26ad808eca1b6077b37272dab03ebdc258f351ee1537")] + [InlineData('a', "1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7ea44f93ee1234aa88f61c91912a4ccd9")] + [InlineData('b', "0c851fd986de48f9703a157327512d705e0aec5e339b53d99f4f3d55b02bd81a513e3ab059d20a348c993acd6591d347")] + [InlineData('c', "fc046ce484597913e61aac55245cc91c7393a9cd69012f22b389ab14ce40ec56f4520cdb1a2054ad3e588e192ec0df5c")] + [InlineData('d', "0312ab38cafbaa6fffe82ab1aeafcce1d4c656c5fde60444232a374df23d6c364c4f33bb044ae258e25111227c9d57da")] + [InlineData('e', "114681f5af3e1c7be7bfb31f60b1bab2de762d43258a23f56f92dececb14949956dc9bda8f248b62a881299064600b4a")] + [InlineData('f', "3f803d234b62ccd243b91510c620c15aaec715c7c46491fb640130322906cf8b52e0e129200459d04746892562cd70d4")] + [InlineData('g', "aea65684980ae4b2c328ccfc045cf27e8ede51c5ed11e85813f7e5709faa7ed3653df2333fa9310f521813a8cc5b80da")] + [InlineData('h', "906c31e68be36e29921029db158e0b4a6b083441e50a40bcca6460d5122bdd1c331cb6b867c9451f48e4745239a6219e")] + [InlineData('i', "a293952c9b39b0ad52e72686526ffb7981cfefa6a9a836b36f87d2f6409b0d41699eb4ad6391a5856978a0f601e43679")] + [InlineData('j', "42a8f1be481d8b95e919ca68bf684bf4476606dcdcbea2924289b6a10a8daa87fa89cdda0c252a77ef841bad5b258bf9")] + [InlineData('k', "b3311bb3e3415615a58fc9001704bf32dec9fb294735783e8195cd479979408e7c36d564934d64a5cea541f3f3bf2195")] + [InlineData('l', "d1a95dab99bdfc5e1f48213027c463387c617881903985572d43433b4e4d8d21e0906abb58c2e4a633cc16abba1a9663")] + [InlineData('m', "5428b66b2021a5090995a9f4964f8f82fb34712d4a10f69f1bfff92f8534e3939d86c83b441fb83bb937055733b9e6d9")] + [InlineData('n', "4303b4457f0cd358a24b43c1454327e2b2cdd56d8975fff4be77119dd8d27291a65d26a1e3233f3b773f6d82d2e78cd6")] + [InlineData('o', "e0133f6d5dd9727860f150073dd6ee154db4e440b65b7d26889e503039d54f5fbfc4646476cd34d808a0089310000dc5")] + [InlineData('p', "c441ea8b76d530b8a4e95e35f6f4eb0d8de7526b8d9972d7f45fb9f5301805c8b73d559e4c8458a001a1a1ad9b563dcc")] + [InlineData('q', "a73cf129eac67f6b9e2f3818b3b845572914c3c6821fafdc71d834f7852ba1c1d894c1a1d71669b9090d1a08418d34d9")] + [InlineData('r', "f01f898eceabaf8863678b738f224815a5746965e7eb1a91d4614f9eef2328791252037a1add0180f7f4f3e7fa66edda")] + [InlineData('s', "e5c105cc135ede693be4be4bb2c9b624a90fa1e422e43b97948ea6ca14f2ffff36b6f586c46110db208999f8a2f0d139")] + [InlineData('t', "63644c71a1fc2e1a2d1aaf429338b0e04e34738ef744f7c6d59ae9a9f96d096fa0852f20b4762c581e604d0178000da8")] + [InlineData('u', "bef30a27a058db0cf7397faa3eee6aad5736618be94a6ea284d2da411ee1f087af864d709d57c0827e647dc722093b69")] + [InlineData('v', "1e5d1e7b4777d1c827f77385016e0ffc832efce7ab2888808f34de90e44b21751e648b902a5dc3ceaf0c847a9727a047")] + [InlineData('w', "045ad9ad61b764222e1a1d8f442d1eb62434805d98e516e2394911949a08a3505629b9e3f2fd27ca25fedcb42bb8dc71")] + [InlineData('x', "5abfc7bc2a09a612f87987ce070634a0932d31891a61a0ec598e81e6ec616c9f00f05ff627070cbf6cb0499b1c334d4d")] + [InlineData('y', "40567ee6c5b13d88390bbec7b4364d1308960652a87ef83208e1bc23867b96638c5c2e3970a91de68a398ecb51452c90")] + [InlineData('z', "2b053962ede73a41a0235fd9e29243b2bbf3513a8b1d586525949fde2f38c659808ee0254dda75ee310185635e42e041")] + [InlineData('0', "17c0608360f9652153b4bf29611b146bbb7ed3336c33d944c8cf7637ffe8ff440b3b0b67a127a183a5d7e2d978f544c5")] + [InlineData('1', "f39de487a8aed2d19069ed7a7bcfc274e9f026bba97c8f059be6a2e5eed051d7ee437b93d80aa6163bf8039543b612dd")] + [InlineData('2', "39773563a8fc5c19ba80f0dc0f57bf49ba0e804abe8e68a1ed067252c30ef499d54ab4eb4e8f4cfa2cfac6c83798997e")] + [InlineData('3', "5f9714f2c47c4ee6af02e96db42b64a3750f5ec5f3541d1a1a6fd20d3632395c55439e208557e782f22a9714885b6e0c")] + [InlineData('4', "d87826dc897a66ee657458dbbe788e473e809b47c93bb37902b74b53999ae64a0ecdc8f76b28b608c2bf66f836d1b8d9")] + [InlineData('5', "d17e08f9fd1ec955b2384bba9312e525edad397e244071a0dd499c3403719434c5c21d833e7ecd46ed47f14d2bdbcfa3")] + [InlineData('6', "7cca6b41713794de552e96349b5f5bf35fa9f12806bfce76a5aa3cdd450e0a98495be64b2023f3188e80cbe27c802d1b")] + [InlineData('7', "4bf873ccb328cdc95a26473588df6c107706c166e240294fc5c70c2b220adc9314e166b0a77344825a34a835cb422ebb")] + [InlineData('8', "854ed8ecc48ed40a6bbf2fc0de3cfbd1811937e23340b245d2d618dc3d5349dbb0fea84e54184557247df6f456731040")] + [InlineData('9', "4894ec28d9d6494918765447867b8fbe65f7a6ec5a30f5aa3ce168c766fb8f9c63cb02602c730e8b259381942ac1f49b")] + public void Sha3Hash384ShouldProduceExpectedResult(char character, string expected) + { + // Given + using HashAlgorithm algorithm = Sha3.CreateSha3Hash384(); + + // When + Hash hash = Hash.Compute(algorithm, [character]); + string actual = hash.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Sha3Hash512 should produce the expected result")] + [InlineData('A', "f5f0eaa9ca3fd0c4e0d72a3471e4b71edaabe2d01c4b25e16715004ed91e663a1750707cc9f04430f19b995f4aba21b0ec878fc5c4eb838a18df5bf9fdc949df")] + [InlineData('B', "7b637bc5543d96f49500aaad3b27d8bd37624db23d415c4d0f3dd231e9b9fb061f39b7d8561c540650de8bef02aca43a2069cc2512697bd34f2244ee732743a9")] + [InlineData('C', "be0c2f5b07cb96d61f0e1e3fd250f0a1709d7388a24e192b9734ae0f9c92abcbd095fed6c5978ad9e55bb62a6a2ca16532eb3f3e5b3bc1e64209546e7e887445")] + [InlineData('D', "eb3eb4ccf7cb3db0ca62080b7ad8a255879c9aa1334a4a5f751687577e3f03b1afde16a38003b029d426b2dd74dfa0fa3cd561fbf79d86bf2c9f966ac154719a")] + [InlineData('E', "41b334d0a39eed4c28639643919433d8976248dd70fbb2b03ed2f233a8820a5de496686c6a81a3e1897ccd8794b92743b42617ab38152f2a8a04e588bad5742d")] + [InlineData('F', "b1845edee6f21b7b4ca587b3b22514972fa681f9380184a8e946a40c432239537b2558a774cd84c28488833fecc9567d57d3a99192983ac931b14f6b0aeda9b2")] + [InlineData('G', "b32657b8079a2eb23041a360c20b8b7fa4c76e300912ccf019ca2e94ec33c898a93b39fa8c1a6f1bba933db63e1a027bf52ee6d23e81aac71ebb2721031a7881")] + [InlineData('H', "8e844c36dda15c37458d23c88a6e66782a5ac90cd0135960b658f4d3e3c783fc2bbf68a0a8e2a054d7d52f9b1f5417bdf4f0e2825fcceb0fe0375466426c1f4c")] + [InlineData('I', "6b8b537691c9252e34bab51bf067ad765065c97f6dfd2c346c8b9f020698187826ed2be1f1669db72a476453341fb0c7cdda49bbf6ec5b55899bf0b64c3e09e1")] + [InlineData('J', "5a3f7519955a950ffb0f9f839649f8a88a20ba2c746da1fef8d97a5c4d9e4b0a2fbd5a8b63ee74a6322a1d0bc3d447717291f3dc0c504f38b4a7ca5998961253")] + [InlineData('K', "afd0107aaf7b3ed73353cf54ca923d2c1f8e75c151a19bf8d2e0b7f873db9b5ce33e6a1db692dcb352fba4b89b1a1ee823f19abd1485e8018f9c71e64bacfd8b")] + [InlineData('L', "6bbac080a582f03fa999fb1cc16a1d856191bd84dbaea946312e980c2ddf5e61bf754b5028f93191d8ff237d6030634001dc8799651062d1a96e44e210599c02")] + [InlineData('M', "e3c3b7d964d08646d4935b2299fd02d79db5b2132059cbf3cef72066871b85812ab2489182c5fbffedadd67f89cccf08b7b744a4c8ff050117a9b9068f508002")] + [InlineData('N', "471126be6139191b7c622abbf162fafaaaa2b46caf5ae11787b2d70d9c96628655a6870e58eaee4218864c136ee5d24e81b39000cefce12fbd407924cafa36e2")] + [InlineData('O', "3d75f1a138ee6317875acc325062b5d4bda8af7a715f372d6bc0e618d8a64feaf462b833471846770cf8648c28a09fc24a97574e52c8a3219e59e2d5048dfeb9")] + [InlineData('P', "4dec0e71e1250d9edcca403f02023fcbbd88be4d0af99a4dc4494ab4505ec07e0660804af0c53c513f1f3e2981ab4d9f7325c37a6cc075ff5a0f438666767030")] + [InlineData('Q', "e95b1fd29022cc854c5c7cf4d4704de993ac8d093bfee5b0c6ede93af431458e8e19c31934125aff62984f574ce3a30f6251a7ac11381e92b4a1f52283e13a07")] + [InlineData('R', "8e48ecc4d3b8a5125db648907fe0ef8ec14b353302779a5c6a7fd8a8142ab97a079602a51f02fdff0bf28dbae03212cfd1538901689f8c90c7425ddac7752d5a")] + [InlineData('S', "329fadd7c1d5f243d57af8a95da82235d2b846681d725e9eaf7095800fa66197144044622c79b37b9e501966e6a35a866ebf03d0b3033e446b993d3f5b193de3")] + [InlineData('T', "7282ac39a48e7cbba327dabc6843ba412970d9ade37e49c5ce8f0360d61a6bc4644a3b9947efa013d8429cac9ca02dd28c1132236c09aca9e57910e3a1a8046f")] + [InlineData('U', "c581306f35e63725c488de906569ed6675e577eab4f75cb1c06b19164c7efa47899d36a365cf228f9b7688820ed0df2b2a4f72a0fadf03a6b2cf3e75440cb6a9")] + [InlineData('V', "6b212db1ff14f2781c78334afc9d9b8d57a2318856823ec7fa78ee3d39b28ce29e8915f4fceab367c44b52acfed0989945e5d7537f9f89553fe47e443d786be5")] + [InlineData('W', "cac54800b600476ec1132e9c58b486bb92ec9bcc4eb3c52ef7bb97ea42eac8b08ea047b605eb9d146b273720c37649b9497bc77f8dee1d327fdeebcbf06729bb")] + [InlineData('X', "c1d414ec5914ecbaa2507f89fe9eaee9eba8ba506bf1e58faf8c7982d4e0329fa8824f7ee8f0258f0b23a304d93997357c4c732d5bdb1288fe7b1af68ba97432")] + [InlineData('Y', "c66a0c202ae9466c93deb74b21c15cf6bc789e5e88b0327486b5476a1aed0e9a2053b549134d041447156aaf4bd1944862d820873873228f52fba106a059c41e")] + [InlineData('Z', "fda430c40ee744a4f06e6a750564e16e80451b1943dbd11f8f8f399b1101a06d723c1f730cc8d996f7b5ba5656c6b963dab711dbaf0eb493978db715ee4ac986")] + [InlineData('a', "697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa803f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a")] + [InlineData('b', "8446c46ee03793ba6e5813ba0db4480008926dd1d19efe2c8eb92f9034da974d2171ae483f29ce3a79ed4fdd621ae1ed14fe12532af95ddd0728779ce5aa842d")] + [InlineData('c', "bfe4d7f7377116dc15f794d902621797b72b32396382de2b6e49d4f1d7eabdfddcfc3bc127bb67f92f9458a5733bb21804e7ccd56b4b6f81049339f477cd279d")] + [InlineData('d', "4668897682ccd2b1ee0cae8dc55947291f819cc59ee126f5bd243b1852577414413aeed5780b5fb11090038715beed1b00714a15b31c8d9674fbdbdf7fd4191c")] + [InlineData('e', "6ebb8a73bfd0459bd575b9dbef6dcb970bb11182591f5ecd7c8c0d771b3269b715fcb84005d542ff74306565a46b3b893f64ca41b8519457ae137f6429dfbb1e")] + [InlineData('f', "10a090626438fdee4f1244562d6a39c56e515dbf70293584c5a20ed2e8e048905ce30af923921276360817aac682ad30f462033d97c00670edeee8281939a60c")] + [InlineData('g', "2ca04c154f94c314a835993acedc2634cb2dc491673b2cafa8906a5a29bfc3eeac19fc4baef80932c46184e0fd458594ab9e7e020ce70a25db393de171d69840")] + [InlineData('h', "26cfb46b8264aa515069b0726c0ed4d1c08587a2f1572fcee6a06b6611ba7802e657791c8e64bf372042bd86208995a9a2a1ef2248d1202137ae65b0906f1ae3")] + [InlineData('i', "baeb91abb764a812dcff52b66a8a92747c07c4ecf08140b73b0e3819abd35cb0634062ca992e74a65bad3c02fc3dc6bac2bd60bc4441771df9ea095842738c39")] + [InlineData('j', "998e8fc13f2160c6dfef0bace960efc019bf173addd5aa3a1dcab50ec8e0d66637b30d0c37f88b09e57477b87b5ddd88b424191ff8e757b384cea28801fe172a")] + [InlineData('k', "8771111f7c5b23494cde7bd3edb03a31e0363d7f76d8ceb07a1300164cf1ef2252e0fcb9e404f6d88785ca2a59f0379d277f4dc7b16f76a726478a639b2da8ce")] + [InlineData('l', "37e275b9f5a7067372be037c5d010e04d3d3ab0df5aab129a9379d9b9c27ecef3f7fa7bffab6582c18323df04c9d0c53cb63fdd5dde484f4227eb60f184756fd")] + [InlineData('m', "7c8e9b49719fda980d594727bd9d3ce693349bba9b303a492726ed7107551879be951f959c55e11b6b1fbd24668f5ee83339da547c04ebc13b56df8b12a03cc0")] + [InlineData('n', "10f12efa3c8edddb5343f970ce1a9037d02764e24e0dceff59f9f66afb3e69158f5ea6666f99f4ad33c47ab9e0c93a25032fd24f1aea6d64ad6b61f7a6dae9bf")] + [InlineData('o', "bb4e922d85066e5ef0a215d59faade03f55728f59a552fc742065d41f189e17dea56a9338764f84bd714f043c2da71d157712921b69a4f23adaebd9366878371")] + [InlineData('p', "9a768ace36ff3d1771d5c145a544de3d68343b2e76093cb7b2a8ea89ac7f1a20c852e6fc1d71275b43abffefac381c5b906f55c3bcff4225353d02f1d3498758")] + [InlineData('q', "f435ba3ef2bf43e694c8940fa315641c67f152c2ee2021f121af5e03f9860607f74e61e1451f9489c2ff59f87dc0e1c501566e2324355de32770ec52cc3bce47")] + [InlineData('r', "d4683a427e18f18bdb592e45c8202bb4f954af961cff43131c358c53980e7936e0e2d2da7fd1babdafddebf8f0ed0ec285637f9fe292241c620633d5524e5122")] + [InlineData('s', "723a86b292df5e128ff2d1945391b8d9f4bb17de3c13f9b8527b1f49fffa2ec69b91b137294aa6b69b78349e20ec17520c338cf33968f86b9383a33d6b0716fc")] + [InlineData('t', "28f9769820918857e4ae2b4e91a41d8624d58fd926f7c3678732d69740c5684db562accf252cb8cd06bc4965186f9d6d5c5c5235c2ebede586d6526bcb3fcd92")] + [InlineData('u', "bb5458b8d4376d42b1f4c18308c8d8a0fe4cf2b97007b6f55d928c41fc19bbf18c47565e0e5ba9ce26612e7327d947769f191cbfc1d542ddd0c05b3c73e4f173")] + [InlineData('v', "b9a3ad42faec4820b964d8027ce03d6a0a73f9a926cf543002c59ec1e2d385060ec71884faaacbc66e651c9e7a8194add6d1467df0419e841a4cbde075cb0182")] + [InlineData('w', "35f8e0298f058a564e4b23e3c45c9a42f5a1e8757bd4be0a0ed40fcdff2bb2e78dfa57bf37267fa619766a61c4842e7b403122350b8195a2990af602cb4226f1")] + [InlineData('x', "0fdb27960308c51467edd49a0f5e0c434c9cca721f4c35bff005feabaf6010e777a1137ee8187c5288af57578d18d502a0bbe4c022f5587541961e10132d9834")] + [InlineData('y', "ab2962a6563627545935edbb63499e632694f0e8766ac52518e58cb1c1289591729d4a6f92636684ba601b56274d715f17ed4885b05a743f38b27aabdfef602f")] + [InlineData('z', "ffe4d7127d5e222ac77ded78b503276294960867d5501eda748bbb741dbc238d1d68f5f4c76f38fdb03a491bd9ec8c1e20403440315ac5e8050946a00409a724")] + [InlineData('0', "2d44da53f305ab94b6365837b9803627ab098c41a6013694f9b468bccb9c13e95b3900365eb58924de7158a54467e984efcfdabdbcc9af9a940d49c51455b04c")] + [InlineData('1', "ca2c70bc13298c5109ee0cb342d014906e6365249005fd4beee6f01aee44edb531231e98b50bf6810de6cf687882b09320fdd5f6375d1f2debd966fbf8d03efa")] + [InlineData('2', "564e1971233e098c26d412f2d4e652742355e616fed8ba88fc9750f869aac1c29cb944175c374a7b6769989aa7a4216198ee12f53bf7827850dfe28540587a97")] + [InlineData('3', "73fb266a903f956a9034d52c2d2793c37fddc32077898f5d871173da1d646fb80bbc21a0522390b75d3bcc88bd78960bdb73be323ad5fc5b3a16089992957d3a")] + [InlineData('4', "37f558134baa535903c6a88931c8122e334368bf951f2cada569b11774ef9795ef6d2ac961d13ee44a0c837db3817bb9db68ac3bdfb8b19a1308618484a9da8f")] + [InlineData('5', "c74bd95b8555275277d4e941c73985b4bcd923b36fcce75968ebb3c5a8d2b1ac411cfae4c2d473bff59a2b7b5ea220f0ac7bb8c880afb32f1b4881d59cc60d85")] + [InlineData('6', "503ad3364d41a2362f28136ee8a9615108277986f52c34ca170b664eb1c663f5e407e9a3084e90017e315b24ba9162021c477e29b3bb1f84a37eea841fe12b9a")] + [InlineData('7', "72ce921155976b88a4a4bf39a4127c4d9e272eccde35ee864963da855f32330c0f8075aafc3a3aadecf498ee7b5e2f9ee3529ea46d97ee0795bd548b41463771")] + [InlineData('8', "f30e8484fa863883156c517514c4e2a9096ec6009f40ebfb9f00666ec58e52e50e64f9074c9182a325a21cc99516b155560f8c48be28f11f2ee73f6945ff7563")] + [InlineData('9', "b55cf27ef01025e3c761a579a63d1c7c1e54e2d12f8f2928c90f5f5516b0d9c71f2fac9e7ccf28c5adf33c3f78d9548ebfed2dc46dea944aed336d1650721487")] + public void Sha3Hash512ShouldProduceExpectedResult(char character, string expected) + { + // Given + using HashAlgorithm algorithm = Sha3.CreateSha3Hash512(); + + // When + Hash hash = Hash.Compute(algorithm, [character]); + string actual = hash.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Sha3Shake128 should produce the expected result")] + [InlineData('A', 16, "a5ba3aeee1525b4ae5439e54cd711f14")] + [InlineData('A', 32, "a5ba3aeee1525b4ae5439e54cd711f14850251e02c5999a53f61374c0ae089ef")] + [InlineData('B', 16, "74865dbf56f0d9c27b76300b1872ba5f")] + [InlineData('B', 32, "74865dbf56f0d9c27b76300b1872ba5f852c2419010ac745bcabf4500db91fc2")] + [InlineData('C', 16, "2e9587f4196d40eccfc86540d53c34f2")] + [InlineData('C', 32, "2e9587f4196d40eccfc86540d53c34f2282cbbdac7e17c873594d93fb849b2b3")] + [InlineData('D', 16, "f0695584e963aa0f01ac26526ffd4061")] + [InlineData('D', 32, "f0695584e963aa0f01ac26526ffd40614d8920fd947503de8155a6f78c7d4449")] + [InlineData('E', 16, "9e677faf367b255042d0ff421c11a097")] + [InlineData('E', 32, "9e677faf367b255042d0ff421c11a097e5f444ebae4051b698c077e1969224ca")] + [InlineData('F', 16, "a2c949bf7c27b871cbca13f0949f9f2d")] + [InlineData('F', 32, "a2c949bf7c27b871cbca13f0949f9f2da9a234f53bda26f7b220ebcf69963870")] + [InlineData('G', 16, "81ca32e51bc34a70ae1a52d1517e1a6a")] + [InlineData('G', 32, "81ca32e51bc34a70ae1a52d1517e1a6a8c895e43bb772ff39e82d079133ab1bd")] + [InlineData('H', 16, "db72f8cc2e23e24e6485137fed6e6dd6")] + [InlineData('H', 32, "db72f8cc2e23e24e6485137fed6e6dd6f5474d929fd5b20a8708c35d5638ba02")] + [InlineData('I', 16, "9264d43898745dcfaf194719f2f4edaf")] + [InlineData('I', 32, "9264d43898745dcfaf194719f2f4edaf3145bd35a7be80187e3b7fec30486ce2")] + [InlineData('J', 16, "5efe2fbaa173fe4a8492e96089f066dc")] + [InlineData('J', 32, "5efe2fbaa173fe4a8492e96089f066dc55d04f038a012363513c6f924e74a7db")] + [InlineData('K', 16, "58a8694c7e0364217702e964eeaf927b")] + [InlineData('K', 32, "58a8694c7e0364217702e964eeaf927b0da95486164f928a607e39561e138dec")] + [InlineData('L', 16, "73706247bac5dbc741be1ad46f3e62f0")] + [InlineData('L', 32, "73706247bac5dbc741be1ad46f3e62f0fb834b6403a62448c672b074ef725065")] + [InlineData('M', 16, "b969a39b683ce8f8e80ac2c08ce116c4")] + [InlineData('M', 32, "b969a39b683ce8f8e80ac2c08ce116c4c6539100b715c2e01a1dc18bcee9e6cd")] + [InlineData('N', 16, "9e1bfdaba56285939e655aba57b078f7")] + [InlineData('N', 32, "9e1bfdaba56285939e655aba57b078f72ddd0bdece0a73c928d61c998ac4569a")] + [InlineData('O', 16, "4ac61591f84dcb84913e5f6be341f100")] + [InlineData('O', 32, "4ac61591f84dcb84913e5f6be341f100dd7b99f606e21a449ba7f7236f65ec09")] + [InlineData('P', 16, "eab4480497429cd15b8bfdb46014d85f")] + [InlineData('P', 32, "eab4480497429cd15b8bfdb46014d85f89b2621a6785d87dc6e1d160354478e6")] + [InlineData('Q', 16, "bd3d67b5dbf85163d78b6cf16df8c94f")] + [InlineData('Q', 32, "bd3d67b5dbf85163d78b6cf16df8c94f5fba33667d77cdea00476314633b50bb")] + [InlineData('R', 16, "613edcc87e371533095dfaf67754de88")] + [InlineData('R', 32, "613edcc87e371533095dfaf67754de886a38f321d418d069b06af1d63ea29d59")] + [InlineData('S', 16, "9934d41dbceac03b9795ef28330708f0")] + [InlineData('S', 32, "9934d41dbceac03b9795ef28330708f0b494b36786222d6dd148d54193c7e217")] + [InlineData('T', 16, "9dd7847b4c29404da0cbaa5d1cb69df4")] + [InlineData('T', 32, "9dd7847b4c29404da0cbaa5d1cb69df451878f576d2c4bef29544ee025367484")] + [InlineData('U', 16, "286668ef63f9546f20767908cf44d5d7")] + [InlineData('U', 32, "286668ef63f9546f20767908cf44d5d77c0fef4c680aa3f61660ae3beac3aacf")] + [InlineData('V', 16, "55360b718e1ed3de9ce9ce7a08361c91")] + [InlineData('V', 32, "55360b718e1ed3de9ce9ce7a08361c916e8c5d0529f0a9f627eb23e2ca3db8d2")] + [InlineData('W', 16, "c4abaca1f7f8691f4623cac449557dfb")] + [InlineData('W', 32, "c4abaca1f7f8691f4623cac449557dfbede55e269c6e0904b01b921d8a2e2dbb")] + [InlineData('X', 16, "a378c3f63abcc74f0ca75f3b5d0ef6f1")] + [InlineData('X', 32, "a378c3f63abcc74f0ca75f3b5d0ef6f112d2fa716edf851d0895f7bef485ee0f")] + [InlineData('Y', 16, "5bf27867460bbc4a67d5f3930078d748")] + [InlineData('Y', 32, "5bf27867460bbc4a67d5f3930078d74831f08d66190c348b77f0da15b1bafb4f")] + [InlineData('Z', 16, "dad14f5a3d2237fd294fb618e14eae83")] + [InlineData('Z', 32, "dad14f5a3d2237fd294fb618e14eae832c0ee3b6c12dd850cb0196c36dc1c204")] + [InlineData('a', 16, "85c8de88d28866bf0868090b3961162b")] + [InlineData('a', 32, "85c8de88d28866bf0868090b3961162bf82392f690d9e4730910f4af7c6ab3ee")] + [InlineData('b', 16, "f0ffa34335ef979fcf79a200874749da")] + [InlineData('b', 32, "f0ffa34335ef979fcf79a200874749da3054fe398bb6d2137d3c98b82df9160f")] + [InlineData('c', 16, "a5c1009bf9fce98b7930ae5fe7aa7ca0")] + [InlineData('c', 32, "a5c1009bf9fce98b7930ae5fe7aa7ca06c7689fe2bd9be8a75373f1a8ffae454")] + [InlineData('d', 16, "5943bb0f0a00c1990d9a1057656bd983")] + [InlineData('d', 32, "5943bb0f0a00c1990d9a1057656bd983b8fbf44363127d15848d3ba54c3561b1")] + [InlineData('e', 16, "bb2b536f0606a0910bf4b98c74c75e96")] + [InlineData('e', 32, "bb2b536f0606a0910bf4b98c74c75e966d0fbcd792ca55e3c768ca2e92234d50")] + [InlineData('f', 16, "2c6c3bcd720350d73d4f46724c098901")] + [InlineData('f', 32, "2c6c3bcd720350d73d4f46724c098901a788d00e2c25ed9d6b789af2c9dc0982")] + [InlineData('g', 16, "7a529e89a30f8e715331b98032cbda63")] + [InlineData('g', 32, "7a529e89a30f8e715331b98032cbda633e3625c51414cbfa8843a568bd39374c")] + [InlineData('h', 16, "ed6dba4b983e0e45039b17d4ff85c81f")] + [InlineData('h', 32, "ed6dba4b983e0e45039b17d4ff85c81f9a829797def86339dec4b4d0f596625c")] + [InlineData('i', 16, "78ac692a457de231fd8e5bda490a1d0f")] + [InlineData('i', 32, "78ac692a457de231fd8e5bda490a1d0fdb6d1274d505385ae196d821fbd2aa51")] + [InlineData('j', 16, "60e284f4daedb9f6afcd8fe698f7dc7f")] + [InlineData('j', 32, "60e284f4daedb9f6afcd8fe698f7dc7fffa8503cb41d4a83709bbbbd449aa843")] + [InlineData('k', 16, "ac20c8330b5c6c163747281430fa7a9c")] + [InlineData('k', 32, "ac20c8330b5c6c163747281430fa7a9c549ca49ee7d675dbb4511b1cd411a615")] + [InlineData('l', 16, "5238d98fc5871541974a81275dd5f1ea")] + [InlineData('l', 32, "5238d98fc5871541974a81275dd5f1eae54c57ebb0393fae9c6fa78accfc09d6")] + [InlineData('m', 16, "c5c955987cf895c48f31a150f56f57b6")] + [InlineData('m', 32, "c5c955987cf895c48f31a150f56f57b61c793fdca932fe909ad487bbdc3dc66a")] + [InlineData('n', 16, "68f8ea7d3b0bd13eb9d7f9c131dccbba")] + [InlineData('n', 32, "68f8ea7d3b0bd13eb9d7f9c131dccbbac2dc89940edc3d70201fded5363d3e44")] + [InlineData('o', 16, "a0b20673d0b9a9642857aed7faf41ed2")] + [InlineData('o', 32, "a0b20673d0b9a9642857aed7faf41ed2c739c5f23ac9e946281c23fd7f88fb5f")] + [InlineData('p', 16, "f884289b7f8c5ab613344841267a9a7b")] + [InlineData('p', 32, "f884289b7f8c5ab613344841267a9a7b2a79b0a0fee49aa0affd2aa4fe1f1643")] + [InlineData('q', 16, "87b8d13b05e63515cb6ee34685ece7c3")] + [InlineData('q', 32, "87b8d13b05e63515cb6ee34685ece7c3be88a65f5b68949a523b0cd68855e716")] + [InlineData('r', 16, "95434212048c97e0a06af33459defb3f")] + [InlineData('r', 32, "95434212048c97e0a06af33459defb3f9cbd95e2fcc0c7f9d572229687ce1dce")] + [InlineData('s', 16, "bc5ac4a37d609be474d3121af546e217")] + [InlineData('s', 32, "bc5ac4a37d609be474d3121af546e2177ef9af4f1ad2a0f5ef26d25de1e27b30")] + [InlineData('t', 16, "ef012cb5d92b651014fa6d9c3118eca5")] + [InlineData('t', 32, "ef012cb5d92b651014fa6d9c3118eca56a41f923e94b7242d1fd743e0c7fb9cf")] + [InlineData('u', 16, "4aed09875d70273d35cc94d52d25d4f0")] + [InlineData('u', 32, "4aed09875d70273d35cc94d52d25d4f0c3ad2c0a8d0d2d4dca4d117834995a1a")] + [InlineData('v', 16, "904f2f17cfe7bd45d5be9da1f931f7f8")] + [InlineData('v', 32, "904f2f17cfe7bd45d5be9da1f931f7f817c98f28f448109840ff585fcc5c3f48")] + [InlineData('w', 16, "1e332829e1b27a670aee5b0c0495b229")] + [InlineData('w', 32, "1e332829e1b27a670aee5b0c0495b2293e0e2d80c012cb9f8afac446c03dcc92")] + [InlineData('x', 16, "e472c5e394f30ff8d5d33803f9593e63")] + [InlineData('x', 32, "e472c5e394f30ff8d5d33803f9593e63e2f3862d48a41ef841e4d6486af49972")] + [InlineData('y', 16, "4cccd98df122b0736a05ea30c361846c")] + [InlineData('y', 32, "4cccd98df122b0736a05ea30c361846c5cb89596679b680d08e58a1c19744913")] + [InlineData('z', 16, "bb66897ef6ac6cbc29c3b14c6f1027f6")] + [InlineData('z', 32, "bb66897ef6ac6cbc29c3b14c6f1027f6f660b290bae53b4592848c3ed2e87885")] + [InlineData('0', 16, "628e79cf7948cd1ca156cee7631a3446")] + [InlineData('0', 32, "628e79cf7948cd1ca156cee7631a3446bc21d4947e0be55c803a0d5c40380b4b")] + [InlineData('1', 16, "ebaf5ccd6f37291d34bade1bbff539e7")] + [InlineData('1', 32, "ebaf5ccd6f37291d34bade1bbff539e76c47afb293c5d53914d492e0bdc24045")] + [InlineData('2', 16, "4e9e3870a3187c0b898817f12c0aaeb7")] + [InlineData('2', 32, "4e9e3870a3187c0b898817f12c0aaeb7b664894185f7955e9b2d5e44b154ead0")] + [InlineData('3', 16, "0a7fddc22e37eaf05b744459f6129fd1")] + [InlineData('3', 32, "0a7fddc22e37eaf05b744459f6129fd1c97cb501aaf497ecb6d5d9b1cfadcbf5")] + [InlineData('4', 16, "f7275a1ebcf0a3d7fc46e235dc236a3d")] + [InlineData('4', 32, "f7275a1ebcf0a3d7fc46e235dc236a3d678ea7c47b642b8aec1d0855d6bc7e4e")] + [InlineData('5', 16, "b485d77fdc221ecb320201c4cd09ee31")] + [InlineData('5', 32, "b485d77fdc221ecb320201c4cd09ee3146aaccb460a998c1b803ab4186ecdd43")] + [InlineData('6', 16, "21d93093fe84db44c4d2769ff7e4f2b5")] + [InlineData('6', 32, "21d93093fe84db44c4d2769ff7e4f2b5dc920dcc58ff7f390cdd4642ef7049d5")] + [InlineData('7', 16, "f99079a8eac6f051fac4e62b17f6bc86")] + [InlineData('7', 32, "f99079a8eac6f051fac4e62b17f6bc86ff0ab03eec648e776cf65781fd9fe997")] + [InlineData('8', 16, "cac75ec753ceb7fcf9e9a9a6d84236c1")] + [InlineData('8', 32, "cac75ec753ceb7fcf9e9a9a6d84236c1d39b8a013bd48e547c5a7409fc9eef3c")] + [InlineData('9', 16, "d8ef0690db21f1f2975bb5a860f7c46b")] + [InlineData('9', 32, "d8ef0690db21f1f2975bb5a860f7c46b92e8383520b71d485cc37b267c247ca1")] + public void Sha3Shake128ShouldProduceExpectedResult(char character, int length, string expected) + { + // Given + using HashAlgorithm algorithm = Sha3.CreateSha3Shake128(length); + + // When + Hash hash = Hash.Compute(algorithm, [character]); + string actual = hash.ToString(); + + // Then + Assert.Equal(expected, actual); + } + + [Theory(DisplayName = "Sha3Shake256 should produce the expected result")] + [InlineData('A', 16, "5e6812c0bbaaee6440dcc8b81ca68096")] + [InlineData('A', 32, "5e6812c0bbaaee6440dcc8b81ca6809645f7512e06cf5acb57bd16dc3a2bfc57")] + [InlineData('B', 16, "9b4033bf5151724308b4b1fc90f15346")] + [InlineData('B', 32, "9b4033bf5151724308b4b1fc90f1534688ea1a17c911aa3a897d5b6a05db5c25")] + [InlineData('C', 16, "6c1fcc1b777f8c560a5c6ac7a21d5d4a")] + [InlineData('C', 32, "6c1fcc1b777f8c560a5c6ac7a21d5d4a73e6948eae9c7c7b93bc5e2085564999")] + [InlineData('D', 16, "7bb2dce81e4f414e23454084c1c11d94")] + [InlineData('D', 32, "7bb2dce81e4f414e23454084c1c11d948cafdf5e85932618ceae8b3f9953c51c")] + [InlineData('E', 16, "4f78bc55b48ab68d5c7c5e91f7a9959d")] + [InlineData('E', 32, "4f78bc55b48ab68d5c7c5e91f7a9959d4b6748f1580382fe6e2170a6c0dbd691")] + [InlineData('F', 16, "ba84a59142ae7d8a245819b992152e95")] + [InlineData('F', 32, "ba84a59142ae7d8a245819b992152e95e167316c74e0cfb6867bf7177f6030a6")] + [InlineData('G', 16, "a01dc253b94528539c20cf5dfefcab12")] + [InlineData('G', 32, "a01dc253b94528539c20cf5dfefcab121de3524f701183a1e830e898705dd7a3")] + [InlineData('H', 16, "fd3d47ec252afaf37ee08bdd346a40bf")] + [InlineData('H', 32, "fd3d47ec252afaf37ee08bdd346a40bf768c1cb68432d01729b91c1c71b5d42e")] + [InlineData('I', 16, "ab44b02fab0fb7917eda9e709efeb696")] + [InlineData('I', 32, "ab44b02fab0fb7917eda9e709efeb6964172fcba345c6fe73ed6219bf77269fb")] + [InlineData('J', 16, "0bc8dcd68d6402c2ae942cf13d968229")] + [InlineData('J', 32, "0bc8dcd68d6402c2ae942cf13d968229e28574a5f94110cd22b97afc373f26bd")] + [InlineData('K', 16, "fba4254262cb96830ca70a363097888a")] + [InlineData('K', 32, "fba4254262cb96830ca70a363097888a21cfe0fcc0668d7a4952a621bb619f6b")] + [InlineData('L', 16, "becd3c8f9f288f6728f3bea68a80a026")] + [InlineData('L', 32, "becd3c8f9f288f6728f3bea68a80a02630e387551bc9530ee1548e37a55f6fd5")] + [InlineData('M', 16, "38613aed74157de1524d2b24f8ebda7f")] + [InlineData('M', 32, "38613aed74157de1524d2b24f8ebda7fa0890b869d50625c4f5512f957efa5da")] + [InlineData('N', 16, "5284ac45c58ae1800442c5d43d067e13")] + [InlineData('N', 32, "5284ac45c58ae1800442c5d43d067e134bd6bbf40c671343a553739d42cf7d22")] + [InlineData('O', 16, "4d9223d5444c05e92d9d0abd37d5020c")] + [InlineData('O', 32, "4d9223d5444c05e92d9d0abd37d5020cb41f10f4a27392c2facc124135b7fdcc")] + [InlineData('P', 16, "f1654d7130e8d85fca768dd011bd5348")] + [InlineData('P', 32, "f1654d7130e8d85fca768dd011bd5348f491e6903576c8e478b9fbe8b86322b2")] + [InlineData('Q', 16, "b64d0e68e2d381fac86172d12f1343b2")] + [InlineData('Q', 32, "b64d0e68e2d381fac86172d12f1343b20fcda532d59daf77eb44c5fc5943f4d7")] + [InlineData('R', 16, "4f6bf07cd877ad550fa2aec9ab3a10af")] + [InlineData('R', 32, "4f6bf07cd877ad550fa2aec9ab3a10af827e9285551c3d4009ceaeb3f9db7699")] + [InlineData('S', 16, "c5c21f9a333d36e7a9f5926e2e4a9883")] + [InlineData('S', 32, "c5c21f9a333d36e7a9f5926e2e4a988386c13126a454cce340b5c3a513a87ad1")] + [InlineData('T', 16, "a695c007e9bba6a81ed1ca7cbb853ecc")] + [InlineData('T', 32, "a695c007e9bba6a81ed1ca7cbb853ecc66415e06ee027550f26ef287ce51f839")] + [InlineData('U', 16, "85ce175af72e5877f858a1307089c207")] + [InlineData('U', 32, "85ce175af72e5877f858a1307089c2074cb5b6573d08f76ed7169bc52b2d1a75")] + [InlineData('V', 16, "0621c3c0c6dbb3d2c4daa2c4d5c66c02")] + [InlineData('V', 32, "0621c3c0c6dbb3d2c4daa2c4d5c66c026d2eabcbbbc55fcf811e77a3b306e7ff")] + [InlineData('W', 16, "77e786c411f0de5be44de0e4b034bbae")] + [InlineData('W', 32, "77e786c411f0de5be44de0e4b034bbaec33a2c31e372847e342f92a0ae2a6fdc")] + [InlineData('X', 16, "5404f315ad1550324f15a8ca77b41d77")] + [InlineData('X', 32, "5404f315ad1550324f15a8ca77b41d7794c0878ec1ef8c3bd800710d47f384f6")] + [InlineData('Y', 16, "cd7b48e3d243c0912de0cca30e7980ae")] + [InlineData('Y', 32, "cd7b48e3d243c0912de0cca30e7980aeedb188bc957aff0ccefd43f663ba2a73")] + [InlineData('Z', 16, "fd48054304d155cff97a6e1c4a531872")] + [InlineData('Z', 32, "fd48054304d155cff97a6e1c4a531872e99c456174bce19a660fdeb025a5427c")] + [InlineData('a', 16, "867e2cb04f5a04dcbd592501a5e8fe9c")] + [InlineData('a', 32, "867e2cb04f5a04dcbd592501a5e8fe9ceaafca50255626ca736c138042530ba4")] + [InlineData('b', 16, "e5796351f59c6264ac1866da170b79de")] + [InlineData('b', 32, "e5796351f59c6264ac1866da170b79de04cecb6317de6b05ca08e42abf32c785")] + [InlineData('c', 16, "b0f080246a623d2588fd6a8427575287")] + [InlineData('c', 32, "b0f080246a623d2588fd6a8427575287703ae4a8451e043d995f1e8f0afc3bd7")] + [InlineData('d', 16, "0142aad0876d139fe243d071563a634d")] + [InlineData('d', 32, "0142aad0876d139fe243d071563a634d56d2e3e1d15a8edf51f16b98d26467d2")] + [InlineData('e', 16, "24e123d7538cf7a23c8f8e6ed94b7e0f")] + [InlineData('e', 32, "24e123d7538cf7a23c8f8e6ed94b7e0f8edaf7aa4a10957583373c341fd4d1e3")] + [InlineData('f', 16, "b967c7cc5249454c318a7fbbbaaeaf36")] + [InlineData('f', 32, "b967c7cc5249454c318a7fbbbaaeaf36309a492e53ab1f604a9ac59cdfb57b52")] + [InlineData('g', 16, "10673dc5dbe94c1db4b4d5f909a954ed")] + [InlineData('g', 32, "10673dc5dbe94c1db4b4d5f909a954eddf17f22a6e05e66724dab315b3e5a84c")] + [InlineData('h', 16, "f877e016a2c3167ca3df83e5346d1b06")] + [InlineData('h', 32, "f877e016a2c3167ca3df83e5346d1b061a0bec1055ce45b729b181fc41bba729")] + [InlineData('i', 16, "3bb3af4ee58cbc3fc615b29774342549")] + [InlineData('i', 32, "3bb3af4ee58cbc3fc615b297743425497be0843a96a20a4a2b69d201c025eaf0")] + [InlineData('j', 16, "cb085dbb162ea9c3f2be3025c861c322")] + [InlineData('j', 32, "cb085dbb162ea9c3f2be3025c861c3222c98e59cb78e4d083a8bab80b0174bb5")] + [InlineData('k', 16, "fb7e1de572ddc648221bb7f112e43fb4")] + [InlineData('k', 32, "fb7e1de572ddc648221bb7f112e43fb4bdd0c11d7927816b70019e18c5209f0b")] + [InlineData('l', 16, "b29190b5fb60edf2f39432e6b65ff662")] + [InlineData('l', 32, "b29190b5fb60edf2f39432e6b65ff66216a9d1952bddf39a5ca98f5733f0716f")] + [InlineData('m', 16, "2be3c6b9a40e58d8498a4cba8492252b")] + [InlineData('m', 32, "2be3c6b9a40e58d8498a4cba8492252b41386129f3aeda88be7bb1f6f6d8a98c")] + [InlineData('n', 16, "b19698e3b6c338ee13082951975ca366")] + [InlineData('n', 32, "b19698e3b6c338ee13082951975ca366c01454b4447e4468664b68b88df81da9")] + [InlineData('o', 16, "fc45b64dee64820667da8ce089f1d670")] + [InlineData('o', 32, "fc45b64dee64820667da8ce089f1d6708059fe9b72145f0d974c00bca7c678ba")] + [InlineData('p', 16, "8de786bfe1d6dea5b32cba7febedd6ff")] + [InlineData('p', 32, "8de786bfe1d6dea5b32cba7febedd6ff144ab0e03fe85108c6270a719d572195")] + [InlineData('q', 16, "2d8e1bec7cd9bdaddff81fac8bf5773f")] + [InlineData('q', 32, "2d8e1bec7cd9bdaddff81fac8bf5773f7413082c1bb11d415b23bf376f872ba2")] + [InlineData('r', 16, "0c4e806212458a793a21390358704530")] + [InlineData('r', 32, "0c4e806212458a793a2139035870453078cba6a4cee11accd88e366db4b3662b")] + [InlineData('s', 16, "aaed5602244df2167017e5c0f2f682ec")] + [InlineData('s', 32, "aaed5602244df2167017e5c0f2f682ec5fa02fd58b7de9a7a264b6b6635b3d97")] + [InlineData('t', 16, "d72a9756dd8b448a638aa7758df2fc00")] + [InlineData('t', 32, "d72a9756dd8b448a638aa7758df2fc006eb3241e0924db117a584cb53b46aa9a")] + [InlineData('u', 16, "4d3948929081f779f08265b0abd3eea9")] + [InlineData('u', 32, "4d3948929081f779f08265b0abd3eea9ca8761783a7aa2532f6a724935de330d")] + [InlineData('v', 16, "6894f9ce4cf4da4028eb279cf53ca8af")] + [InlineData('v', 32, "6894f9ce4cf4da4028eb279cf53ca8afa78946cbfe6cfa1b3e5b672e107b20c9")] + [InlineData('w', 16, "83901a8d9dc152e79f2bfd4ec75790bd")] + [InlineData('w', 32, "83901a8d9dc152e79f2bfd4ec75790bd774546aa54c7590f82b6fea524ceb075")] + [InlineData('x', 16, "c851121abe8095973c9bdc1b446089f3")] + [InlineData('x', 32, "c851121abe8095973c9bdc1b446089f3249cf74e0b64660b61d73bcae0350f4d")] + [InlineData('y', 16, "0c0e7900a42d04a00afef323858bc461")] + [InlineData('y', 32, "0c0e7900a42d04a00afef323858bc461deb73fbf01343d3de7dee05f40f8057a")] + [InlineData('z', 16, "b9bb5914c3385c795584905092480f74")] + [InlineData('z', 32, "b9bb5914c3385c795584905092480f74cf633b662308cc1744c39136625b44c1")] + [InlineData('0', 16, "7e8b1406d903bc9137fb69e769742c8d")] + [InlineData('0', 32, "7e8b1406d903bc9137fb69e769742c8d3e36f1c4fed51a608809b08de9f3e4a0")] + [InlineData('1', 16, "2f169f9b4e6a1024752209cd5410ebb8")] + [InlineData('1', 32, "2f169f9b4e6a1024752209cd5410ebb84959eee0ac73c29a04c23bd524c12f81")] + [InlineData('2', 16, "a5a4f007abc4dfe1eb19f685efde94ca")] + [InlineData('2', 32, "a5a4f007abc4dfe1eb19f685efde94ca76f77dff7279de620dd52074b33fa1c6")] + [InlineData('3', 16, "08946cd494a2c00b0e9321af0c225309")] + [InlineData('3', 32, "08946cd494a2c00b0e9321af0c225309e9d1b9d14ce8eeb4ed5182031c3f29b0")] + [InlineData('4', 16, "1d8a904c4fff579bc28fd3a8065762b9")] + [InlineData('4', 32, "1d8a904c4fff579bc28fd3a8065762b958f81089579cf2177ae7489a90f7d396")] + [InlineData('5', 16, "172f84a65934fc29776758a22ad080b3")] + [InlineData('5', 32, "172f84a65934fc29776758a22ad080b341b497b1967d89a20dbd8420f4d4507b")] + [InlineData('6', 16, "cc2dc8d8adb6439605fa188ed5f0d8a4")] + [InlineData('6', 32, "cc2dc8d8adb6439605fa188ed5f0d8a43930b8e1eb8fc46e63dd9ab6a643910d")] + [InlineData('7', 16, "112a104bd5901f13abbfdcd11be28abf")] + [InlineData('7', 32, "112a104bd5901f13abbfdcd11be28abfeea892133b1861afe6cc4c999cc9c160")] + [InlineData('8', 16, "09dfb269bed6186424d76680f5b936b8")] + [InlineData('8', 32, "09dfb269bed6186424d76680f5b936b858b844472cbc5e1ea59d24282e8b3e31")] + [InlineData('9', 16, "0d869764040d76f626be277bc31072f1")] + [InlineData('9', 32, "0d869764040d76f626be277bc31072f1e85d9376223b23584817a2ba9834304f")] + public void Sha3Shake256ShouldProduceExpectedResult(char character, int length, string expected) + { + // Given + using HashAlgorithm algorithm = Sha3.CreateSha3Shake256(length); + + // When + Hash hash = Hash.Compute(algorithm, [character]); + string actual = hash.ToString(); + + // Then + Assert.Equal(expected, actual); + } +} diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.Equatable.cs b/OnixLabs.Security.Cryptography/DigitalSignature.Equatable.cs index 837007e..6d6eed8 100644 --- a/OnixLabs.Security.Cryptography/DigitalSignature.Equatable.cs +++ b/OnixLabs.Security.Cryptography/DigitalSignature.Equatable.cs @@ -1,73 +1,72 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Linq; using OnixLabs.Core.Linq; namespace OnixLabs.Security.Cryptography; -public readonly partial struct DigitalSignature : IEquatable +public readonly partial struct DigitalSignature { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(DigitalSignature a, DigitalSignature b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(DigitalSignature other) { - return Equals(a, b); + return value.SequenceEqual(other.value); } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(DigitalSignature a, DigitalSignature b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return obj is DigitalSignature other && Equals(other); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public bool Equals(DigitalSignature other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return other.Value.SequenceEqual(Value); + return value.GetContentHashCode(); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(DigitalSignature left, DigitalSignature right) { - return obj is DigitalSignature other && Equals(other); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(DigitalSignature left, DigitalSignature right) { - return HashCode.Combine(Value.GetContentHashCode()); + return !Equals(left, right); } } diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.From.cs b/OnixLabs.Security.Cryptography/DigitalSignature.From.cs deleted file mode 100644 index 27a39b1..0000000 --- a/OnixLabs.Security.Cryptography/DigitalSignature.From.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct DigitalSignature -{ - /// - /// Creates a instance from the specified digitally signed data. - /// - /// The digitally signed data. - /// Returns a instance from the specified digitally signed data. - public static DigitalSignature FromByteArray(byte[] value) - { - return new DigitalSignature(value); - } - - /// - /// Creates a from the specified value. - /// - /// The Base-16 from which to construct a signature value. - /// Returns an from the specified Base-16 value. - public static DigitalSignature FromBase16(Base16 value) - { - byte[] bytes = value.ToByteArray(); - return FromByteArray(bytes); - } - - /// - /// Creates a from the specified value. - /// - /// The Base-32 from which to construct a signature value. - /// Returns an from the specified Base-32 value. - public static DigitalSignature FromBase32(Base32 value) - { - byte[] bytes = value.ToByteArray(); - return FromByteArray(bytes); - } - - /// - /// Creates a from the specified value. - /// - /// The Base-58 from which to construct a signature value. - /// Returns an from the specified Base-58 value. - public static DigitalSignature FromBase58(Base58 value) - { - byte[] bytes = value.ToByteArray(); - return FromByteArray(bytes); - } - - /// - /// Creates a from the specified value. - /// - /// The Base-64 from which to construct a signature value. - /// Returns an from the specified Base-64 value. - public static DigitalSignature FromBase64(Base64 value) - { - byte[] bytes = value.ToByteArray(); - return FromByteArray(bytes); - } -} diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.To.cs b/OnixLabs.Security.Cryptography/DigitalSignature.To.cs index 85cf729..73e4f4f 100644 --- a/OnixLabs.Security.Cryptography/DigitalSignature.To.cs +++ b/OnixLabs.Security.Cryptography/DigitalSignature.To.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,83 +14,26 @@ using System; using OnixLabs.Core; -using OnixLabs.Core.Text; namespace OnixLabs.Security.Cryptography; public readonly partial struct DigitalSignature { /// - /// Returns a byte array containing the underlying signed data. + /// Gets the underlying representation of the current instance. /// - /// A byte array containing the underlying signed data. + /// Return the underlying representation of the current instance. public byte[] ToByteArray() { - return Value.Copy(); + return value.Copy(); } /// - /// Returns a value that represents the underlying signature data. + /// Returns a that represents the current object. /// - /// Returns a value that represents the underlying signature data. - public Base16 ToBase16() - { - return Base16.FromByteArray(Value); - } - - /// - /// Returns a value that represents the underlying signature data. - /// - /// Returns a value that represents the underlying signature data. - public Base32 ToBase32() - { - return ToBase32(Base32Alphabet.Default); - } - - /// - /// Returns a value that represents the underlying signature data. - /// - /// The Base-32 alphabet to use to encode the signature data. - /// Returns a value that represents the underlying signature data. - public Base32 ToBase32(Base32Alphabet alphabet) - { - return Base32.FromByteArray(Value, alphabet); - } - - /// - /// Returns a value that represents the underlying signature data. - /// - /// Returns a value that represents the underlying signature data. - public Base58 ToBase58() - { - return ToBase58(Base58Alphabet.Default); - } - - /// - /// Returns a value that represents the underlying signature data. - /// - /// The Base-58 alphabet to use to encode the signature data. - /// Returns a value that represents the underlying signature data. - public Base58 ToBase58(Base58Alphabet alphabet) - { - return Base58.FromByteArray(Value, alphabet); - } - - /// - /// Returns a value that represents the underlying signature data. - /// - /// Returns a value that represents the underlying signature data. - public Base64 ToBase64() - { - return Base64.FromByteArray(Value); - } - - /// - /// Returns a that represents the current object. - /// - /// A that represents the current object. + /// Returns a that represents the current object. public override string ToString() { - return Convert.ToHexString(Value).ToLower(); + return Convert.ToHexString(value).ToLower(); } } diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.Validation.cs b/OnixLabs.Security.Cryptography/DigitalSignature.Validation.cs deleted file mode 100644 index 10920cf..0000000 --- a/OnixLabs.Security.Cryptography/DigitalSignature.Validation.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Security.Cryptography; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct DigitalSignature -{ - /// - /// Determines whether this is valid, given the specified unsigned data and public key. - /// - /// The unsigned data to validate. - /// The public key to validate. - /// Returns true if this is valid; otherwise, false. - public bool IsDataValid(byte[] unsignedData, PublicKey key) - { - return key.IsDataValid(this, unsignedData); - } - - /// - /// Determines whether this is valid, given the specified unsigned hash and public key. - /// - /// The unsigned data to validate. - /// The public key to validate. - /// Returns true if this is valid; otherwise, false. - public bool IsHashValid(byte[] unsignedHash, PublicKey key) - { - return key.IsHashValid(this, unsignedHash); - } - - /// - /// Determines whether this is valid, given the specified unsigned hash and public key. - /// - /// The unsigned data to validate. - /// The public key to validate. - /// Returns true if this is valid; otherwise, false. - public bool IsHashValid(Hash unsignedHash, PublicKey key) - { - byte[] unsignedHashBytes = unsignedHash.ToByteArray(); - return IsHashValid(unsignedHashBytes, key); - } - - /// - /// Verifies this . - /// - /// The unsigned data to verify. - /// The public key to verify. - /// If this was not signed by the specified key. - public void VerifyData(byte[] unsignedData, PublicKey key) - { - if (!IsDataValid(unsignedData, key)) - { - throw new CryptographicException("The specified digital signature was not signed with this key."); - } - } - - /// - /// Verifies this . - /// - /// The unsigned hash to verify. - /// The public key to verify. - /// If this was not signed by the specified key. - public void VerifyHash(byte[] unsignedHash, PublicKey key) - { - if (!IsHashValid(unsignedHash, key)) - { - throw new CryptographicException("The specified digital signature was not signed with this key."); - } - } - - /// - /// Verifies this . - /// - /// The unsigned hash to verify. - /// The public key to verify. - /// If this was not signed by the specified key. - public void VerifyHash(Hash unsignedHash, PublicKey key) - { - byte[] unsignedHashBytes = unsignedHash.ToByteArray(); - VerifyHash(unsignedHashBytes, key); - } -} diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.cs b/OnixLabs.Security.Cryptography/DigitalSignature.cs index 6ffb8d1..43d8b4b 100644 --- a/OnixLabs.Security.Cryptography/DigitalSignature.cs +++ b/OnixLabs.Security.Cryptography/DigitalSignature.cs @@ -1,35 +1,26 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Security.Cryptography; /// -/// Represents a digital signature. +/// Represents a cryptographic digital signature. /// -public readonly partial struct DigitalSignature +/// The underlying value of the cryptographic digital signature. +public readonly partial struct DigitalSignature(ReadOnlySpan value) : ICryptoPrimitive { - /// - /// Initializes a new instance of the struct. - /// - /// The digitally signed data. - private DigitalSignature(byte[] value) - { - Value = value; - } - - /// - /// Gets the underlying digitally signed data. - /// - private byte[] Value { get; } + private readonly byte[] value = value.ToArray(); } diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Create.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Create.cs new file mode 100644 index 0000000..532c2fc --- /dev/null +++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Create.cs @@ -0,0 +1,55 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Security.Cryptography; + +namespace OnixLabs.Security.Cryptography; + +public sealed partial class EcdsaPrivateKey +{ + /// + /// Creates a new ECDSA cryptographic private key. + /// + /// Returns a new instance. + public static EcdsaPrivateKey Create() + { + using ECDsa key = ECDsa.Create(); + byte[] keyData = key.ExportECPrivateKey(); + return new EcdsaPrivateKey(keyData); + } + + /// + /// Creates a new ECDSA cryptographic private key. + /// + /// The elliptic curve from which to create a new ECDSA cryptographic private key. + /// Returns a new instance. + public static EcdsaPrivateKey Create(ECCurve curve) + { + using ECDsa key = ECDsa.Create(curve); + byte[] keyData = key.ExportECPrivateKey(); + return new EcdsaPrivateKey(keyData); + } + + /// + /// Creates a new ECDSA cryptographic private key. + /// + /// The elliptic curve parameters from which to create a new ECDSA cryptographic private key. + /// Returns a new instance. + public static EcdsaPrivateKey Create(ECParameters parameters) + { + using ECDsa key = ECDsa.Create(parameters); + byte[] keyData = key.ExportECPrivateKey(); + return new EcdsaPrivateKey(keyData); + } +} diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Export.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Export.cs index 73f6f0f..42a4ce4 100644 --- a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Export.cs +++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Export.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,30 +20,24 @@ namespace OnixLabs.Security.Cryptography; public sealed partial class EcdsaPrivateKey { /// - /// Exports the key in PKCS #8 format. + /// Exports the ECDSA cryptographic private key data in PKCS #8 format. /// - /// Returns the key in PKCS #8 format. - public override byte[] ExportPkcs8Key() + /// Returns a new instance containing the ECDSA cryptographic private key data in PKCS #8 format. + public override byte[] ExportPkcs8PrivateKey() { - using ECDsa privateKey = ECDsa.Create(); - - privateKey.ImportECPrivateKey(KeyData, out int _); - - return privateKey.ExportPkcs8PrivateKey(); + using ECDsa key = ImportKeyData(); + return key.ExportPkcs8PrivateKey(); } /// - /// Exports the key in encrypted PKCS #8 format. + /// Exports the ECDSA cryptographic private key data in encrypted PKCS #8 format. /// /// The password to use for encryption. /// The parameters required for password based encryption. - /// Returns the key in encrypted PKCS #8 format. - public override byte[] ExportPkcs8Key(ReadOnlySpan password, PbeParameters parameters) + /// Returns a new instance containing the ECDSA cryptographic private key data in PKCS #8 format. + public override byte[] ExportPkcs8PrivateKey(ReadOnlySpan password, PbeParameters parameters) { - using ECDsa privateKey = ECDsa.Create(); - - privateKey.ImportECPrivateKey(KeyData, out int _); - - return privateKey.ExportEncryptedPkcs8PrivateKey(password, parameters); + using ECDsa key = ImportKeyData(); + return key.ExportEncryptedPkcs8PrivateKey(password, parameters); } } diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.From.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.From.cs deleted file mode 100644 index e43b3b0..0000000 --- a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.From.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class EcdsaPrivateKey -{ - /// - /// Creates an from the specified key data and hash algorithm type. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPrivateKey FromByteArray(byte[] key, HashAlgorithmType type) - { - return new EcdsaPrivateKey(key, type); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPrivateKey FromBase16(Base16 key, HashAlgorithmType type) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPrivateKey FromBase32(Base32 key, HashAlgorithmType type) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPrivateKey FromBase58(Base58 key, HashAlgorithmType type) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPrivateKey FromBase64(Base64 key, HashAlgorithmType type) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type); - } -} diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Get.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Get.cs index 91cfb79..63f7102 100644 --- a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Get.cs +++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Get.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,16 +19,13 @@ namespace OnixLabs.Security.Cryptography; public sealed partial class EcdsaPrivateKey { /// - /// Gets the public key component from this private key. + /// Gets the ECDSA cryptographic public key component from the current ECDSA cryptographic private key. /// - /// Returns the public key component from this private key. - public override PublicKey GetPublicKey() + /// Returns a new instance containing the ECDSA cryptographic public key component from the current ECDSA cryptographic private key. + public override EcdsaPublicKey GetPublicKey() { - using ECDsa privateKey = ECDsa.Create(); - - privateKey.ImportECPrivateKey(KeyData, out int _); - byte[] publicKey = privateKey.ExportSubjectPublicKeyInfo(); - - return new EcdsaPublicKey(publicKey, AlgorithmType); + using ECDsa key = ImportKeyData(); + byte[] keyData = key.ExportSubjectPublicKeyInfo(); + return new EcdsaPublicKey(keyData); } } diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Import.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Import.cs index ac3559e..6650422 100644 --- a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Import.cs +++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Import.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,112 +20,63 @@ namespace OnixLabs.Security.Cryptography; public sealed partial class EcdsaPrivateKey { /// - /// Imports a PKCS #8 formatted key. + /// Imports the ECDSA cryptographic private key data in PKCS #8 format. /// - /// The key data to import. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static EcdsaPrivateKey ImportPkcs8Key(ReadOnlySpan data, HashAlgorithmType type) + /// The cryptographic private key data to import. + /// Returns a new instance from the imported cryptographic private key data. + public static EcdsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data) { - ECDsa privateKey = ECDsa.Create(); - - privateKey.ImportPkcs8PrivateKey(data, out int _); - byte[] bytes = privateKey.ExportECPrivateKey(); - - return FromByteArray(bytes, type); - } - - /// - /// Imports a PKCS #8 formatted key. - /// - /// The key data to import. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static EcdsaPrivateKey ImportPkcs8Key(byte[] data, HashAlgorithmType type) - { - ReadOnlySpan bytes = data.AsSpan(); - return ImportPkcs8Key(bytes, type); - } - - /// - /// Imports an encrypted PKCS #8 formatted key. - /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static EcdsaPrivateKey ImportPkcs8Key(ReadOnlySpan data, ReadOnlySpan password, HashAlgorithmType type) - { - ECDsa privateKey = ECDsa.Create(); - - privateKey.ImportEncryptedPkcs8PrivateKey(password, data, out int _); - byte[] bytes = privateKey.ExportECPrivateKey(); - - return FromByteArray(bytes, type); - } - - /// - /// Imports an encrypted PKCS #8 formatted key. - /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static EcdsaPrivateKey ImportPkcs8Key(ReadOnlySpan data, char[] password, HashAlgorithmType type) - { - ReadOnlySpan characters = password.AsSpan(); - return ImportPkcs8Key(data, characters, type); + return ImportPkcs8PrivateKey(data, out int _); } /// - /// Imports an encrypted PKCS #8 formatted key. + /// Imports the ECDSA cryptographic private key data in PKCS #8 format. /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static EcdsaPrivateKey ImportPkcs8Key(ReadOnlySpan data, string password, HashAlgorithmType type) + /// The cryptographic private key data to import. + /// The number of bytes read from the input data. + /// Returns a new instance from the imported cryptographic private key data. + public static EcdsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, out int bytesRead) { - ReadOnlySpan characters = password.AsSpan(); - return ImportPkcs8Key(data, characters, type); + using ECDsa key = ECDsa.Create(); + key.ImportPkcs8PrivateKey(data, out bytesRead); + byte[] keyData = key.ExportECPrivateKey(); + return new EcdsaPrivateKey(keyData); } /// - /// Imports an encrypted PKCS #8 formatted key. + /// Imports the ECDSA cryptographic private key data in encrypted PKCS #8 format. /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static EcdsaPrivateKey ImportPkcs8Key(byte[] data, ReadOnlySpan password, HashAlgorithmType type) + /// The cryptographic private key data to import. + /// The password required for password based decryption. + /// Returns a new instance from the imported cryptographic private key data. + public static EcdsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, ReadOnlySpan password) { - ReadOnlySpan bytes = data.AsSpan(); - return ImportPkcs8Key(bytes, password, type); + return ImportPkcs8PrivateKey(data, password, out int _); } /// - /// Imports an encrypted PKCS #8 formatted key. + /// Imports the ECDSA cryptographic private key data in encrypted PKCS #8 format. /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static EcdsaPrivateKey ImportPkcs8Key(byte[] data, char[] password, HashAlgorithmType type) + /// The cryptographic private key data to import. + /// The password required for password based decryption. + /// The number of bytes read from the input data. + /// Returns a new instance from the imported cryptographic private key data. + public static EcdsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, ReadOnlySpan password, out int bytesRead) { - ReadOnlySpan bytes = data.AsSpan(); - return ImportPkcs8Key(bytes, password, type); + using ECDsa key = ECDsa.Create(); + key.ImportEncryptedPkcs8PrivateKey(password, data, out bytesRead); + byte[] keyData = key.ExportECPrivateKey(); + return new EcdsaPrivateKey(keyData); } /// - /// Imports an encrypted PKCS #8 formatted key. + /// Imports the key data into a new instance. /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static EcdsaPrivateKey ImportPkcs8Key(byte[] data, string password, HashAlgorithmType type) + /// Returns a new instance containing the imported key data. + private ECDsa ImportKeyData() { - ReadOnlySpan bytes = data.AsSpan(); - return ImportPkcs8Key(bytes, password, type); + ECDsa algorithm = ECDsa.Create(); + algorithm.ImportECPrivateKey(KeyData, out int _); + return algorithm; } } diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Sign.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Sign.cs index a47a64d..652c2d7 100644 --- a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Sign.cs +++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Sign.cs @@ -1,51 +1,155 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.IO; using System.Security.Cryptography; +using OnixLabs.Core; namespace OnixLabs.Security.Cryptography; public sealed partial class EcdsaPrivateKey { /// - /// Computes a from the specified unsigned data. + /// Hashes the specified data and signs the resulting hash. /// - /// The unsigned data from which to compute a . - /// Returns a from the specified unsigned data. - public override DigitalSignature SignData(byte[] unsignedData) + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default) { - using ECDsa privateKey = ECDsa.Create(); + byte[] hash = algorithm.ComputeHash(data.ToArray()); + return SignHash(hash, format); + } + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data.ToArray(), offset, count); + return SignHash(hash, format); + } + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data); + return SignHash(hash, format); + } + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data.ToByteArray()); + return SignHash(hash, format); + } + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.SignData(data, algorithm, format); + } - privateKey.ImportECPrivateKey(KeyData, out int _); - HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName(); - byte[] signedData = privateKey.SignData(unsignedData, name); + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.SignData(data.ToArray(), offset, count, algorithm, format); + } - return DigitalSignature.FromByteArray(signedData); + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.SignData(data, algorithm, format); } /// - /// Computes a from the specified unsigned hash. + /// Hashes the specified data and signs the resulting hash. /// - /// The unsigned hash from which to compute a . - /// Returns a from the specified unsigned hash. - public override DigitalSignature SignHash(byte[] unsignedHash) + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default) { - using ECDsa privateKey = ECDsa.Create(); + using ECDsa key = ImportKeyData(); + return key.SignData(data.ToByteArray(), algorithm, format); + } - privateKey.ImportECPrivateKey(KeyData, out int _); - byte[] signedData = privateKey.SignHash(unsignedHash); + /// + /// Signs the specified . + /// + /// The hash to sign. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignHash(Hash hash, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.SignHash(hash.ToByteArray(), format); + } - return DigitalSignature.FromByteArray(signedData); + /// + /// Signs the specified . + /// + /// The hash to sign. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignHash(ReadOnlySpan hash, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.SignHash(hash, format); } } diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.cs index 48fad76..2f1f65c 100644 --- a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.cs +++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.cs @@ -1,30 +1,23 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Security.Cryptography; /// -/// Represents an ECDSA private key. +/// Represents an ECDSA cryptographic private key. /// -public sealed partial class EcdsaPrivateKey : PrivateKey -{ - /// - /// Creates a new instance of the class. - /// - /// The private key data. - /// The hash algorithm type for computing signature data. - internal EcdsaPrivateKey(byte[] data, HashAlgorithmType type) : base(data, type) - { - } -} +/// The underlying key data of the ECDSA cryptographic private key. +public sealed partial class EcdsaPrivateKey(ReadOnlySpan keyData) : PrivateKey(keyData), IEcdsaPrivateKey; diff --git a/OnixLabs.Security.Cryptography/EcdsaPublicKey.From.cs b/OnixLabs.Security.Cryptography/EcdsaPublicKey.From.cs deleted file mode 100644 index 125755f..0000000 --- a/OnixLabs.Security.Cryptography/EcdsaPublicKey.From.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class EcdsaPublicKey -{ - /// - /// Creates an from the specified key data and hash algorithm type. - /// - /// The key data from which to construct a public key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPublicKey FromByteArray(byte[] key, HashAlgorithmType type) - { - return new EcdsaPublicKey(key, type); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPublicKey FromBase16(Base16 key, HashAlgorithmType type) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPublicKey FromBase32(Base32 key, HashAlgorithmType type) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPublicKey FromBase58(Base58 key, HashAlgorithmType type) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static EcdsaPublicKey FromBase64(Base64 key, HashAlgorithmType type) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Empty.cs b/OnixLabs.Security.Cryptography/EcdsaPublicKey.Import.cs similarity index 58% rename from OnixLabs.Security.Cryptography/Hmac.Empty.cs rename to OnixLabs.Security.Cryptography/EcdsaPublicKey.Import.cs index dcf141d..a642f50 100644 --- a/OnixLabs.Security.Cryptography/Hmac.Empty.cs +++ b/OnixLabs.Security.Cryptography/EcdsaPublicKey.Import.cs @@ -1,33 +1,31 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; +using System.Security.Cryptography; namespace OnixLabs.Security.Cryptography; -public readonly partial struct Hmac +public sealed partial class EcdsaPublicKey { /// - /// Gets an empty hashed message authentication code (HMAC) value. + /// Imports the key data into a new instance. /// - public static readonly Hmac Empty; - - /// - /// Initializes static members of the class. - /// - static Hmac() + /// Returns a new instance containing the imported key data. + private ECDsa ImportKeyData() { - Empty = Create(Hash.Empty, Array.Empty()); + ECDsa algorithm = ECDsa.Create(); + algorithm.ImportSubjectPublicKeyInfo(KeyData, out int _); + return algorithm; } } diff --git a/OnixLabs.Security.Cryptography/EcdsaPublicKey.Verify.cs b/OnixLabs.Security.Cryptography/EcdsaPublicKey.Verify.cs index bd44b43..1191106 100644 --- a/OnixLabs.Security.Cryptography/EcdsaPublicKey.Verify.cs +++ b/OnixLabs.Security.Cryptography/EcdsaPublicKey.Verify.cs @@ -1,53 +1,666 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.IO; using System.Security.Cryptography; +using OnixLabs.Core; namespace OnixLabs.Security.Cryptography; public sealed partial class EcdsaPublicKey { /// - /// Determines whether the specified was signed by the private component of this public key. + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. /// - /// The to validate. - /// The unsigned data to validate. - /// Returns true if the specified was signed by the private component of this public key; otherwise, false. - public override bool IsDataValid(DigitalSignature signature, byte[] unsignedData) + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default) { - using ECDsa publicKey = ECDsa.Create(); + byte[] hash = algorithm.ComputeHash(data.ToArray()); + return IsHashValid(signature, hash, format); + } - publicKey.ImportSubjectPublicKeyInfo(KeyData, out int _); - byte[] signatureData = signature.ToByteArray(); - HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName(); + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data.ToArray(), offset, count); + return IsHashValid(signature, hash, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data); + return IsHashValid(signature, hash, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data.ToByteArray()); + return IsHashValid(signature, hash, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyData(data, signature, algorithm, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyData(data.ToArray(), offset, count, signature.ToArray(), algorithm, format); + } - return publicKey.VerifyData(unsignedData, signatureData, name); + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyData(data, signature.ToArray(), algorithm, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyData(data.ToByteArray(), signature, algorithm, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data.ToArray()); + return IsHashValid(signature, hash, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data.ToArray(), offset, count); + return IsHashValid(signature, hash, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data); + return IsHashValid(signature, hash, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + byte[] hash = algorithm.ComputeHash(data.ToByteArray()); + return IsHashValid(signature, hash, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyData(data, signature.ToByteArray(), algorithm, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyData(data.ToArray(), offset, count, signature.ToByteArray(), algorithm, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyData(data, signature.ToByteArray(), algorithm, format); + } + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyData(data.ToByteArray(), signature.ToByteArray(), algorithm, format); + } + + /// + /// Determines whether the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsHashValid(ReadOnlySpan signature, Hash hash, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyHash(hash.ToByteArray(), signature, format); + } + + /// + /// Determines whether the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsHashValid(ReadOnlySpan signature, ReadOnlySpan hash, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyHash(hash, signature, format); + } + + /// + /// Determines whether the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsHashValid(DigitalSignature signature, Hash hash, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyHash(hash.ToByteArray(), signature.ToByteArray(), format); + } + + /// + /// Determines whether the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsHashValid(DigitalSignature signature, ReadOnlySpan hash, DSASignatureFormat format = default) + { + using ECDsa key = ImportKeyData(); + return key.VerifyHash(hash, signature.ToByteArray(), format); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, offset, count, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(ReadOnlySpan signature, Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, offset, count, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(ReadOnlySpan signature, Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(DigitalSignature signature, ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, offset, count, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(DigitalSignature signature, Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(DigitalSignature signature, IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(DigitalSignature signature, ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, offset, count, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(DigitalSignature signature, Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyData(DigitalSignature signature, IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default) + { + CheckSignature(IsDataValid(signature, data, algorithm, format)); + } + + /// + /// Verifies that the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyHash(ReadOnlySpan signature, Hash hash, DSASignatureFormat format = default) + { + CheckSignature(IsHashValid(signature, hash, format)); + } + + /// + /// Verifies that the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyHash(ReadOnlySpan signature, ReadOnlySpan hash, DSASignatureFormat format = default) + { + CheckSignature(IsHashValid(signature, hash, format)); + } + + /// + /// Verifies that the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyHash(DigitalSignature signature, Hash hash, DSASignatureFormat format = default) + { + CheckSignature(IsHashValid(signature, hash, format)); + } + + /// + /// Verifies that the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + public void VerifyHash(DigitalSignature signature, ReadOnlySpan hash, DSASignatureFormat format = default) + { + CheckSignature(IsHashValid(signature, hash, format)); } /// - /// Determines whether the specified was signed by the private component of this public key. + /// Performs a signature check pre-condition that throws a in the event that the specified condition is . /// - /// The to validate. - /// The unsigned hash to validate. - /// Returns true if the specified was signed by the private component of this public key; otherwise, false. - public override bool IsHashValid(DigitalSignature signature, byte[] unsignedHash) + /// The signature condition to check. + /// If the specified condition is . + private static void CheckSignature(bool condition) { - using ECDsa publicKey = ECDsa.Create(); + if (condition) return; - publicKey.ImportSubjectPublicKeyInfo(KeyData, out int _); - byte[] signatureData = signature.ToByteArray(); + const string message = "The specified digital signature could not be verified. Either the specified data is incorrect, " + + "or the data was not signed with a private key corresponding to the current public key."; - return publicKey.VerifyHash(unsignedHash, signatureData); + throw new CryptographicException(message); } } diff --git a/OnixLabs.Security.Cryptography/EcdsaPublicKey.cs b/OnixLabs.Security.Cryptography/EcdsaPublicKey.cs index 1af352b..97f92c3 100644 --- a/OnixLabs.Security.Cryptography/EcdsaPublicKey.cs +++ b/OnixLabs.Security.Cryptography/EcdsaPublicKey.cs @@ -1,30 +1,23 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Security.Cryptography; /// -/// Represents an ECDSA public key. +/// Represents an ECDSA cryptographic public key. /// -public sealed partial class EcdsaPublicKey : PublicKey -{ - /// - /// Creates a new instance of the class. - /// - /// The public key data. - /// The hash algorithm type for computing signature data. - internal EcdsaPublicKey(byte[] data, HashAlgorithmType type) : base(data, type) - { - } -} +/// The underlying key data of the ECDSA cryptographic public key. +public sealed partial class EcdsaPublicKey(ReadOnlySpan keyData) : PublicKey(keyData), IEcdsaPublicKey; diff --git a/OnixLabs.Security.Cryptography/Extensions.HashAlgorithm.cs b/OnixLabs.Security.Cryptography/Extensions.HashAlgorithm.cs new file mode 100644 index 0000000..97f9678 --- /dev/null +++ b/OnixLabs.Security.Cryptography/Extensions.HashAlgorithm.cs @@ -0,0 +1,103 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.ComponentModel; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +/// +/// Provides extension methods for hash algorithms. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class HashAlgorithmExtensions +{ + /// + /// Computes the hash value for the specified byte array. + /// + /// The which will be used to compute a hash value. + /// The input data to compute the hash for. + /// The number of rounds that the input data should be hashed. + /// Returns the computed hash value. + public static byte[] ComputeHash(this HashAlgorithm algorithm, byte[] data, int rounds) + { + return algorithm.ComputeHash(new MemoryStream(data), rounds); + } + + /// + /// Computes the hash value for the specified byte array. + /// + /// The which will be used to compute a hash value. + /// The input data to compute the hash for. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The number of rounds that the input data should be hashed. + /// Returns the computed hash value. + public static byte[] ComputeHash(this HashAlgorithm algorithm, byte[] data, int offset, int count, int rounds) + { + return algorithm.ComputeHash(data.Copy(offset, count), rounds); + } + + /// + /// Computes the hash value for the specified . + /// + /// The which will be used to compute a hash value. + /// The input data to compute the hash for. + /// The which will be used to convert the specified . + /// The number of rounds that the input data should be hashed. + /// Returns the computed hash value. + public static byte[] ComputeHash(this HashAlgorithm algorithm, ReadOnlySpan data, Encoding? encoding = null, int rounds = 1) + { + return algorithm.ComputeHash((encoding ?? Encoding.Default).GetBytes(data.ToArray()), rounds); + } + + /// + /// Computes the hash value for the specified object. + /// + /// The which will be used to compute a hash value. + /// The input data to compute the hash for. + /// The number of rounds that the input data should be hashed. + /// Returns the computed hash value. + public static byte[] ComputeHash(this HashAlgorithm algorithm, Stream stream, int rounds) + { + Require(rounds > 0, "Rounds must be greater than zero", nameof(rounds)); + + byte[] data = algorithm.ComputeHash(stream); + while (--rounds > 0) data = algorithm.ComputeHash(data); + return data; + } + + /// + /// Asynchronously computes the hash value for the specified object. + /// + /// The which will be used to compute a hash value. + /// The input data to compute the hash for. + /// The number of rounds that the input data should be hashed. + /// The token to monitor for cancellation requests. + /// Returns a task that represents the asynchronous compute hash operation and wraps the computed hash value. + public static async Task ComputeHashAsync(this HashAlgorithm algorithm, Stream stream, int rounds, CancellationToken token = default) + { + Require(rounds > 0, "Rounds must be greater than zero", nameof(rounds)); + + MemoryStream memoryStream = new(await algorithm.ComputeHashAsync(stream, token)); + while (--rounds > 0) memoryStream = new MemoryStream(await algorithm.ComputeHashAsync(memoryStream, token)); + return memoryStream.ToArray(); + } +} diff --git a/OnixLabs.Security.Cryptography/Hash.AllOneHash.cs b/OnixLabs.Security.Cryptography/Hash.AllOneHash.cs deleted file mode 100644 index 94beba4..0000000 --- a/OnixLabs.Security.Cryptography/Hash.AllOneHash.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using static OnixLabs.Core.Preconditions; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Creates an all-one hash of the specified length. This will create a hash of an unknown type. - /// - /// The length of the hash in bytes. - /// Returns an all-one of the specified length. - public static Hash CreateAllOneHash(int length) - { - return CreateAllOneHash(HashAlgorithmType.Unknown, length); - } - - /// - /// Creates an all-one hash of the specified hash algorithm type. - /// - /// The type of hash to create. - /// Returns an all-one of the specified hash algorithm type. - public static Hash CreateAllOneHash(HashAlgorithmType type) - { - return CreateAllOneHash(type, type.Length); - } - - /// - /// Creates an all-one hash of the specified hash algorithm type and length. - /// - /// The type of hash to create. - /// The length of the hash in bytes. - /// Returns an all-one of the specified hash algorithm type and length. - /// If the length of the hash is unexpected. - public static Hash CreateAllOneHash(HashAlgorithmType type, int length) - { - Check(type.IsUnknown || type.Length == length, "Unexpected hash algorithm output length."); - - byte[] bytes = Enumerable.Repeat(byte.MaxValue, length).ToArray(); - return FromByteArray(bytes, type); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.AllZeroHash.cs b/OnixLabs.Security.Cryptography/Hash.AllZeroHash.cs deleted file mode 100644 index 5985b43..0000000 --- a/OnixLabs.Security.Cryptography/Hash.AllZeroHash.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using static OnixLabs.Core.Preconditions; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Creates an all-zero hash of the specified length. This will create a hash of an unknown type. - /// - /// The length of the hash in bytes. - /// Returns an all-zero of the specified length. - public static Hash CreateAllZeroHash(int length) - { - return CreateAllZeroHash(HashAlgorithmType.Unknown, length); - } - - /// - /// Creates an all-zero hash of the specified hash algorithm type. - /// - /// The type of hash to create. - /// Returns an all-zero of the specified hash algorithm type. - public static Hash CreateAllZeroHash(HashAlgorithmType type) - { - return CreateAllZeroHash(type, type.Length); - } - - /// - /// Creates an all-zero hash of the specified hash algorithm type and length. - /// - /// The type of hash to create. - /// The length of the hash in bytes. - /// Returns an all-zero of the specified hash algorithm type and length. - /// If the length of the hash is unexpected. - public static Hash CreateAllZeroHash(HashAlgorithmType type, int length) - { - Check(type.IsUnknown || type.Length == length, "Unexpected hash algorithm output length."); - - byte[] bytes = Enumerable.Repeat(byte.MinValue, length).ToArray(); - return FromByteArray(bytes, type); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Comparable.cs b/OnixLabs.Security.Cryptography/Hash.Comparable.cs new file mode 100644 index 0000000..15418f8 --- /dev/null +++ b/OnixLabs.Security.Cryptography/Hash.Comparable.cs @@ -0,0 +1,92 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Numerics; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +public readonly partial struct Hash +{ + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates + /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the + /// other object. + /// + /// An object to compare with this instance. + /// Returns a value that indicates the relative order of the objects being compared. + public int CompareTo(Hash other) + { + BigInteger left = new(value); + BigInteger right = new(other.value); + + return left.CompareTo(right); + } + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates + /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the + /// other object. + /// + /// An object to compare with this instance. + /// Returns a value that indicates the relative order of the objects being compared. + public int CompareTo(object? obj) + { + return this.CompareToObject(obj); + } + + /// + /// Performs a greater than comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is greater than the right-hand instance; otherwise, . + public static bool operator >(Hash left, Hash right) + { + return left.CompareTo(right) is 1; + } + + /// + /// Performs a greater than or equal comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is greater than or equal to the right-hand instance; otherwise, . + public static bool operator >=(Hash left, Hash right) + { + return left.CompareTo(right) is 0 or 1; + } + + /// + /// Performs a less than comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is less than the right-hand instance; otherwise, . + public static bool operator <(Hash left, Hash right) + { + return left.CompareTo(right) is -1; + } + + /// + /// Performs a less than or equal comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is less than or equal to the right-hand instance; otherwise, . + public static bool operator <=(Hash left, Hash right) + { + return left.CompareTo(right) is 0 or -1; + } +} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Md5Hash.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Md5Hash.cs deleted file mode 100644 index 3272729..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Md5Hash.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes an MD5 hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeMd5Hash(string value) - { - return ComputeMd5Hash(value, Encoding.Default); - } - - /// - /// Computes an MD5 hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeMd5Hash(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Md5Hash, encoding); - } - - /// - /// Computes an MD5 hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeMd5Hash(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Md5Hash); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha1Hash.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha1Hash.cs deleted file mode 100644 index f55d934..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha1Hash.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-1 hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha1Hash(string value) - { - return ComputeSha1Hash(value, Encoding.Default); - } - - /// - /// Computes a SHA-1 hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeSha1Hash(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Sha1Hash, encoding); - } - - /// - /// Computes a SHA-1 hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha1Hash(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Sha1Hash); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash256.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash256.cs deleted file mode 100644 index e19fd63..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash256.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-2 256-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha2Hash256(string value) - { - return ComputeSha2Hash256(value, Encoding.Default); - } - - /// - /// Computes a SHA-2 256-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeSha2Hash256(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Sha2Hash256, encoding); - } - - /// - /// Computes a SHA-2 256-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha2Hash256(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Sha2Hash256); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash384.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash384.cs deleted file mode 100644 index 8b1c0f1..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash384.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-2 384-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha2Hash384(string value) - { - return ComputeSha2Hash384(value, Encoding.Default); - } - - /// - /// Computes a SHA-2 384-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeSha2Hash384(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Sha2Hash384, encoding); - } - - /// - /// Computes a SHA-2 384-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha2Hash384(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Sha2Hash384); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash512.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash512.cs deleted file mode 100644 index d310bd6..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash512.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-2 512-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha2Hash512(string value) - { - return ComputeSha2Hash512(value, Encoding.Default); - } - - /// - /// Computes a SHA-2 512-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeSha2Hash512(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Sha2Hash512, encoding); - } - - /// - /// Computes a SHA-2 512-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha2Hash512(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Sha2Hash512); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash224.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash224.cs deleted file mode 100644 index 64e6a74..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash224.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 224-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha3Hash224(string value) - { - return ComputeSha3Hash224(value, Encoding.Default); - } - - /// - /// Computes a SHA-3 224-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeSha3Hash224(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Sha3Hash224, encoding); - } - - /// - /// Computes a SHA-3 224-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha3Hash224(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Sha3Hash224); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash256.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash256.cs deleted file mode 100644 index 6346596..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash256.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 256-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha3Hash256(string value) - { - return ComputeSha3Hash256(value, Encoding.Default); - } - - /// - /// Computes a SHA-3 256-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeSha3Hash256(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Sha3Hash256, encoding); - } - - /// - /// Computes a SHA-3 256-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha3Hash256(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Sha3Hash256); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash384.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash384.cs deleted file mode 100644 index 6ab1e42..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash384.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 384-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha3Hash384(string value) - { - return ComputeSha3Hash384(value, Encoding.Default); - } - - /// - /// Computes a SHA-3 384-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeSha3Hash384(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Sha3Hash384, encoding); - } - - /// - /// Computes a SHA-3 384-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha3Hash384(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Sha3Hash384); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash512.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash512.cs deleted file mode 100644 index 6c2c34b..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash512.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 512-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha3Hash512(string value) - { - return ComputeSha3Hash512(value, Encoding.Default); - } - - /// - /// Computes a SHA-3 512-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static Hash ComputeSha3Hash512(string value, Encoding encoding) - { - return ComputeHash(value, HashAlgorithmType.Sha3Hash512, encoding); - } - - /// - /// Computes a SHA-3 512-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static Hash ComputeSha3Hash512(byte[] value) - { - return ComputeHash(value, HashAlgorithmType.Sha3Hash512); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake128.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake128.cs deleted file mode 100644 index c94f57b..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake128.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 Shake 128-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static Hash ComputeSha3Shake128(string value, int length) - { - return ComputeSha3Shake128(value, Encoding.Default, length); - } - - /// - /// Computes a SHA-3 Shake 128-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static Hash ComputeSha3Shake128(string value, Encoding encoding, int length) - { - return ComputeHash(value, HashAlgorithmType.Sha3Shake128, encoding, length); - } - - /// - /// Computes a SHA-3 Shake 128-bit hash from the specified value. - /// - /// The input value to hash. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static Hash ComputeSha3Shake128(byte[] value, int length) - { - return ComputeHash(value, HashAlgorithmType.Sha3Shake128, length); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake256.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake256.cs deleted file mode 100644 index d1cd45e..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake256.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 Shake 256-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static Hash ComputeSha3Shake256(string value, int length) - { - return ComputeSha3Shake256(value, Encoding.Default, length); - } - - /// - /// Computes a SHA-3 Shake 256-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static Hash ComputeSha3Shake256(string value, Encoding encoding, int length) - { - return ComputeHash(value, HashAlgorithmType.Sha3Shake256, encoding, length); - } - - /// - /// Computes a SHA-3 Shake 256-bit hash from the specified value. - /// - /// The input value to hash. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static Hash ComputeSha3Shake256(byte[] value, int length) - { - return ComputeHash(value, HashAlgorithmType.Sha3Shake256, length); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.cs b/OnixLabs.Security.Cryptography/Hash.Compute.cs index 32ebeea..f70848d 100644 --- a/OnixLabs.Security.Cryptography/Hash.Compute.cs +++ b/OnixLabs.Security.Cryptography/Hash.Compute.cs @@ -1,96 +1,145 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.IO; using System.Security.Cryptography; using System.Text; +using System.Threading; +using System.Threading.Tasks; namespace OnixLabs.Security.Cryptography; public readonly partial struct Hash { /// - /// Computes the hash of the specified value using the specified . + /// Computes the hash of the specified data, using the specified . /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// Returns a representing a hash of the specified value. - public static Hash ComputeHash(string value, HashAlgorithmType type) + /// The which will be used to compute the hash. + /// The data from which to compute a hash. + /// Returns a cryptographic hash of the specified data. + public static Hash Compute(HashAlgorithm algorithm, byte[] data) { - return ComputeHash(value, type, Encoding.Default); + byte[] value = algorithm.ComputeHash(data); + return new Hash(value); } /// - /// Computes the hash of the specified value using the specified . + /// Computes the hash of the specified data, using the specified . /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a representing a hash of the specified value. - public static Hash ComputeHash(string value, HashAlgorithmType type, Encoding encoding) + /// The which will be used to compute the hash. + /// The data from which to compute a hash. + /// The number of rounds that the input data should be hashed. + /// Returns a cryptographic hash of the specified data. + public static Hash Compute(HashAlgorithm algorithm, byte[] data, int rounds) { - return ComputeHash(encoding.GetBytes(value), type); + byte[] value = algorithm.ComputeHash(data, rounds); + return new Hash(value); } /// - /// Computes the hash of the specified value using the specified . + /// Computes the hash of the specified data, using the specified . /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The output length of the computed hash in bytes. - /// Returns a representing a hash of the specified value. - public static Hash ComputeHash(string value, HashAlgorithmType type, int length) + /// The which will be used to compute the hash. + /// The data from which to compute a hash. + /// The offset into the from which to begin using data. + /// The number of bytes in the array to use as data. + /// Returns a cryptographic hash of the specified data. + public static Hash Compute(HashAlgorithm algorithm, byte[] data, int offset, int count) { - return ComputeHash(value, type, Encoding.Default, length); + byte[] value = algorithm.ComputeHash(data, offset, count); + return new Hash(value); } /// - /// Computes the hash of the specified value using the specified . + /// Computes the hash of the specified data, using the specified . /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The encoding which will be used to convert the input string into a byte array. - /// The output length of the computed hash in bytes. - /// Returns a representing a hash of the specified value. - public static Hash ComputeHash(string value, HashAlgorithmType type, Encoding encoding, int length) + /// The which will be used to compute the hash. + /// The data from which to compute a hash. + /// The offset into the from which to begin using data. + /// The number of bytes in the array to use as data. + /// The number of rounds that the input data should be hashed. + /// Returns a cryptographic hash of the specified data. + public static Hash Compute(HashAlgorithm algorithm, byte[] data, int offset, int count, int rounds) { - return ComputeHash(encoding.GetBytes(value), type, length); + byte[] value = algorithm.ComputeHash(data, offset, count, rounds); + return new Hash(value); } /// - /// Computes the hash of the specified value using the specified . + /// Computes the hash of the specified , using the specified . /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// Returns a representing a hash of the specified value. - public static Hash ComputeHash(byte[] value, HashAlgorithmType type) + /// The which will be used to compute the hash. + /// The data from which to compute a hash. + /// Returns a cryptographic hash of the specified data. + public static Hash Compute(HashAlgorithm algorithm, Stream stream) { - using HashAlgorithm algorithm = type.GetHashAlgorithm(); - byte[] hashedValue = algorithm.ComputeHash(value); - return FromByteArray(hashedValue, type); + byte[] value = algorithm.ComputeHash(stream); + return new Hash(value); } /// - /// Computes the hash of the specified value using the specified . + /// Computes the hash of the specified , using the specified . /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The output length of the computed hash in bytes. - /// Returns a representing a hash of the specified value. - public static Hash ComputeHash(byte[] value, HashAlgorithmType type, int length) + /// The which will be used to compute the hash. + /// The data from which to compute a hash. + /// The number of rounds that the input data should be hashed. + /// Returns a cryptographic hash of the specified data. + public static Hash Compute(HashAlgorithm algorithm, Stream stream, int rounds) { - using HashAlgorithm algorithm = type.GetHashAlgorithm(length); - byte[] hashedValue = algorithm.ComputeHash(value); - return FromByteArray(hashedValue, type); + byte[] value = algorithm.ComputeHash(stream, rounds); + return new Hash(value); + } + + /// + /// Computes the hash value for the specified . + /// + /// The which will be used to compute a hash value. + /// The input data to compute the hash for. + /// The which will be used to convert the specified . + /// The number of rounds that the input data should be hashed. + /// Returns a cryptographic hash of the specified data. + public static Hash Compute(HashAlgorithm algorithm, ReadOnlySpan data, Encoding? encoding = null, int rounds = 1) + { + byte[] value = algorithm.ComputeHash(data, encoding, rounds); + return new Hash(value); + } + + /// + /// Asynchronously computes the hash of the specified data, using the specified . + /// + /// The which will be used to compute the hash. + /// The data from which to compute a hash. + /// The token to monitor for cancellation requests. + /// Returns a cryptographic hash of the specified data. + public static async Task ComputeAsync(HashAlgorithm algorithm, Stream stream, CancellationToken token = default) + { + byte[] value = await algorithm.ComputeHashAsync(stream, token); + return new Hash(value); + } + + /// + /// Asynchronously computes the hash of the specified data, using the specified . + /// + /// The which will be used to compute the hash. + /// The data from which to compute a hash. + /// The number of rounds that the input data should be hashed. + /// The token to monitor for cancellation requests. + /// Returns a cryptographic hash of the specified data. + public static async Task ComputeAsync(HashAlgorithm algorithm, Stream stream, int rounds, CancellationToken token = default) + { + byte[] value = await algorithm.ComputeHashAsync(stream, rounds, token); + return new Hash(value); } } diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Md5Hash.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Md5Hash.cs deleted file mode 100644 index 65c5509..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Md5Hash.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes an MD5 hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeMd5HashAsync(string value) - { - return await ComputeMd5HashAsync(value, Encoding.Default); - } - - /// - /// Computes an MD5 hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeMd5HashAsync(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Md5Hash, encoding); - } - - /// - /// Computes an MD5 hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeMd5HashAsync(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Md5Hash); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha1Hash.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha1Hash.cs deleted file mode 100644 index 9261255..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha1Hash.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-1 hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha1HashAsync(string value) - { - return await ComputeSha1HashAsync(value, Encoding.Default); - } - - /// - /// Computes a SHA-1 hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeSha1HashAsync(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha1Hash, encoding); - } - - /// - /// Computes a SHA-1 hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha1HashAsync(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha1Hash); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash256.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash256.cs deleted file mode 100644 index 3fa2372..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash256.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-2 256-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha2Hash256Async(string value) - { - return await ComputeSha2Hash256Async(value, Encoding.Default); - } - - /// - /// Computes a SHA-2 256-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeSha2Hash256Async(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha2Hash256, encoding); - } - - /// - /// Computes a SHA-2 256-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha2Hash256Async(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha2Hash256); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash384.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash384.cs deleted file mode 100644 index 0e329c6..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash384.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-2 384-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha2Hash384Async(string value) - { - return await ComputeSha2Hash384Async(value, Encoding.Default); - } - - /// - /// Computes a SHA-2 384-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeSha2Hash384Async(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha2Hash384, encoding); - } - - /// - /// Computes a SHA-2 384-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha2Hash384Async(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha2Hash384); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash512.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash512.cs deleted file mode 100644 index 43d7fbc..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha2Hash512.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-2 512-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha2Hash512Async(string value) - { - return await ComputeSha2Hash512Async(value, Encoding.Default); - } - - /// - /// Computes a SHA-2 512-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeSha2Hash512Async(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha2Hash512, encoding); - } - - /// - /// Computes a SHA-2 512-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha2Hash512Async(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha2Hash512); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash224.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash224.cs deleted file mode 100644 index 165702a..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash224.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 224-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha3Hash224Async(string value) - { - return await ComputeSha3Hash224Async(value, Encoding.Default); - } - - /// - /// Computes a SHA-3 224-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeSha3Hash224Async(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Hash224, encoding); - } - - /// - /// Computes a SHA-3 224-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha3Hash224Async(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Hash224); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash256.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash256.cs deleted file mode 100644 index 19378c7..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash256.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 256-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha3Hash256Async(string value) - { - return await ComputeSha3Hash256Async(value, Encoding.Default); - } - - /// - /// Computes a SHA-3 256-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeSha3Hash256Async(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Hash256, encoding); - } - - /// - /// Computes a SHA-3 256-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha3Hash256Async(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Hash256); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash384.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash384.cs deleted file mode 100644 index b48436a..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash384.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 384-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha3Hash384Async(string value) - { - return await ComputeSha3Hash384Async(value, Encoding.Default); - } - - /// - /// Computes a SHA-3 384-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeSha3Hash384Async(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Hash384, encoding); - } - - /// - /// Computes a SHA-3 384-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha3Hash384Async(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Hash384); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash512.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash512.cs deleted file mode 100644 index a6717c9..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Hash512.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 512-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha3Hash512Async(string value) - { - return await ComputeSha3Hash512Async(value, Encoding.Default); - } - - /// - /// Computes a SHA-3 512-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a of the input value. - public static async Task ComputeSha3Hash512Async(string value, Encoding encoding) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Hash512, encoding); - } - - /// - /// Computes a SHA-3 512-bit hash from the specified value. - /// - /// The input value to hash. - /// Returns a of the input value. - public static async Task ComputeSha3Hash512Async(byte[] value) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Hash512); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Shake128.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Shake128.cs deleted file mode 100644 index 102ac35..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Shake128.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 Shake 128-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static async Task ComputeSha3Shake128Async(string value, int length) - { - return await ComputeSha3Shake128Async(value, Encoding.Default, length); - } - - /// - /// Computes a SHA-3 Shake 128-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static async Task ComputeSha3Shake128Async(string value, Encoding encoding, int length) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Shake128, encoding, length); - } - - /// - /// Computes a SHA-3 Shake 128-bit hash from the specified value. - /// - /// The input value to hash. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static async Task ComputeSha3Shake128Async(byte[] value, int length) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Shake128, length); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Shake256.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Shake256.cs deleted file mode 100644 index 96e5d02..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.Sha3Shake256.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a SHA-3 Shake 256-bit hash from the specified value. - /// This will use the default encoding to convert the input string into a byte array. - /// - /// The input value to hash. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static async Task ComputeSha3Shake256Async(string value, int length) - { - return await ComputeSha3Shake256Async(value, Encoding.Default, length); - } - - /// - /// Computes a SHA-3 Shake 256-bit hash from the specified value. - /// - /// The input value to hash. - /// The encoding which will be used to convert the input string into a byte array. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static async Task ComputeSha3Shake256Async(string value, Encoding encoding, int length) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Shake256, encoding, length); - } - - /// - /// Computes a SHA-3 Shake 256-bit hash from the specified value. - /// - /// The input value to hash. - /// The length of the hash in bytes. - /// Returns a of the input value. - public static async Task ComputeSha3Shake256Async(byte[] value, int length) - { - return await ComputeHashAsync(value, HashAlgorithmType.Sha3Shake256, length); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.cs b/OnixLabs.Security.Cryptography/Hash.ComputeAsync.cs deleted file mode 100644 index 04f8532..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeAsync.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.IO; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes the hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// Returns a representing a hash of the specified value. - public static async Task ComputeHashAsync(string value, HashAlgorithmType type) - { - return await ComputeHashAsync(value, type, Encoding.Default); - } - - /// - /// Computes the hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a representing a hash of the specified value. - public static async Task ComputeHashAsync(string value, HashAlgorithmType type, Encoding encoding) - { - return await ComputeHashAsync(encoding.GetBytes(value), type); - } - - /// - /// Computes the hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The output length of the computed hash in bytes. - /// Returns a representing a hash of the specified value. - public static async Task ComputeHashAsync(string value, HashAlgorithmType type, int length) - { - return await ComputeHashAsync(value, type, Encoding.Default, length); - } - - /// - /// Computes the hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The encoding which will be used to convert the input string into a byte array. - /// The output length of the computed hash in bytes. - /// Returns a representing a hash of the specified value. - public static async Task ComputeHashAsync(string value, HashAlgorithmType type, Encoding encoding, int length) - { - return await ComputeHashAsync(encoding.GetBytes(value), type, length); - } - - /// - /// Computes the hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// Returns a representing a hash of the specified value. - public static async Task ComputeHashAsync(byte[] value, HashAlgorithmType type) - { - using HashAlgorithm algorithm = type.GetHashAlgorithm(); - await using Stream stream = new MemoryStream(value); - byte[] hashedValue = await algorithm.ComputeHashAsync(stream); - return FromByteArray(hashedValue, type); - } - - /// - /// Computes the hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The output length of the computed hash in bytes. - /// Returns a representing a hash of the specified value. - public static async Task ComputeHashAsync(byte[] value, HashAlgorithmType type, int length) - { - using HashAlgorithm algorithm = type.GetHashAlgorithm(length); - await using Stream stream = new MemoryStream(value); - byte[] hashedValue = await algorithm.ComputeHashAsync(stream); - return FromByteArray(hashedValue, type); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeTwice.cs b/OnixLabs.Security.Cryptography/Hash.ComputeTwice.cs deleted file mode 100644 index e4b0eb7..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeTwice.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Security.Cryptography; -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// Returns a representing a twice-hashed hash of the specified value. - public static Hash ComputeHashTwice(string value, HashAlgorithmType type) - { - return ComputeHashTwice(value, type, Encoding.Default); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a representing a twice-hashed hash of the specified value. - public static Hash ComputeHashTwice(string value, HashAlgorithmType type, Encoding encoding) - { - return ComputeHashTwice(encoding.GetBytes(value), type); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The output length of the computed hash in bytes. - /// Returns a representing a twice-hashed hash of the specified value. - public static Hash ComputeHashTwice(string value, HashAlgorithmType type, int length) - { - return ComputeHashTwice(value, type, Encoding.Default, length); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The encoding which will be used to convert the input string into a byte array. - /// The output length of the computed hash in bytes. - /// Returns a representing a twice-hashed hash of the specified value. - public static Hash ComputeHashTwice(string value, HashAlgorithmType type, Encoding encoding, int length) - { - return ComputeHashTwice(encoding.GetBytes(value), type, length); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// Returns a representing a twice-hashed hash of the specified value. - public static Hash ComputeHashTwice(byte[] value, HashAlgorithmType type) - { - using HashAlgorithm algorithm = type.GetHashAlgorithm(); - byte[] firstRoundValue = algorithm.ComputeHash(value); - byte[] secondRoundValue = algorithm.ComputeHash(firstRoundValue); - return FromByteArray(secondRoundValue, type); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The output length of the computed hash in bytes. - /// Returns a representing a twice-hashed hash of the specified value. - public static Hash ComputeHashTwice(byte[] value, HashAlgorithmType type, int length) - { - using HashAlgorithm algorithm = type.GetHashAlgorithm(length); - byte[] firstRoundValue = algorithm.ComputeHash(value); - byte[] secondRoundValue = algorithm.ComputeHash(firstRoundValue); - return FromByteArray(secondRoundValue, type); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeTwiceAsync.cs b/OnixLabs.Security.Cryptography/Hash.ComputeTwiceAsync.cs deleted file mode 100644 index 7d05fd1..0000000 --- a/OnixLabs.Security.Cryptography/Hash.ComputeTwiceAsync.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.IO; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// Returns a representing a twice-hashed hash of the specified value. - public static async Task ComputeHashTwiceAsync(string value, HashAlgorithmType type) - { - return await ComputeHashTwiceAsync(value, type, Encoding.Default); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The encoding which will be used to convert the input string into a byte array. - /// Returns a representing a twice-hashed hash of the specified value. - public static async Task ComputeHashTwiceAsync(string value, HashAlgorithmType type, Encoding encoding) - { - return await ComputeHashTwiceAsync(encoding.GetBytes(value), type); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The output length of the computed hash in bytes. - /// Returns a representing a twice-hashed hash of the specified value. - public static async Task ComputeHashTwiceAsync(string value, HashAlgorithmType type, int length) - { - return await ComputeHashTwiceAsync(value, type, Encoding.Default, length); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The encoding which will be used to convert the input string into a byte array. - /// The output length of the computed hash in bytes. - /// Returns a representing a twice-hashed hash of the specified value. - public static async Task ComputeHashTwiceAsync(string value, HashAlgorithmType type, Encoding encoding, int length) - { - return await ComputeHashTwiceAsync(encoding.GetBytes(value), type, length); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// Returns a representing a twice-hashed hash of the specified value. - public static async Task ComputeHashTwiceAsync(byte[] value, HashAlgorithmType type) - { - using HashAlgorithm algorithm = type.GetHashAlgorithm(); - - await using Stream firstRoundStream = new MemoryStream(value); - byte[] firstRoundValue = await algorithm.ComputeHashAsync(firstRoundStream); - - await using Stream secondRoundStream = new MemoryStream(firstRoundValue); - byte[] secondRoundValue = await algorithm.ComputeHashAsync(secondRoundStream); - - return FromByteArray(secondRoundValue, type); - } - - /// - /// Computes a twice-hashed hash of the specified value using the specified . - /// - /// The value for which to compute a hash. - /// The hash algorithm type of the computed hash. - /// The output length of the computed hash in bytes. - /// Returns a representing a twice-hashed hash of the specified value. - public static async Task ComputeHashTwiceAsync(byte[] value, HashAlgorithmType type, int length) - { - using HashAlgorithm algorithm = type.GetHashAlgorithm(length); - - await using Stream firstRoundStream = new MemoryStream(value); - byte[] firstRoundValue = await algorithm.ComputeHashAsync(firstRoundStream); - - await using Stream secondRoundStream = new MemoryStream(firstRoundValue); - byte[] secondRoundValue = await algorithm.ComputeHashAsync(secondRoundStream); - - return FromByteArray(secondRoundValue, type); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Concantenate.cs b/OnixLabs.Security.Cryptography/Hash.Concantenate.cs deleted file mode 100644 index 05163bf..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Concantenate.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using System.Security.Cryptography; -using static OnixLabs.Core.Preconditions; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Concatenates two hashes. - /// - /// The first to concatenate. - /// The second to concatenate. - /// Returns the concatenation of the two instances. - public static Hash Concatenate(Hash a, Hash b) - { - Check(a.AlgorithmType == b.AlgorithmType, "Cannot concatenate hashes of different algorithm types."); - - using HashAlgorithm algorithm = a.AlgorithmType.GetHashAlgorithm(); - byte[] concatenatedValue = a.ToByteArray().Concat(b.ToByteArray()).ToArray(); - byte[] hashedValue = algorithm.ComputeHash(concatenatedValue); - - return FromByteArray(hashedValue, a.AlgorithmType); - } - - /// - /// Concatenates two hashes. - /// - /// The first to concatenate. - /// The second to concatenate. - /// The length of the hash in bytes. - /// Returns the concatenation of the two instances. - public static Hash Concatenate(Hash a, Hash b, int length) - { - Check(a.AlgorithmType == b.AlgorithmType, "Cannot concatenate hashes of different algorithm types."); - - using HashAlgorithm algorithm = a.AlgorithmType.GetHashAlgorithm(length); - byte[] concatenatedValue = a.ToByteArray().Concat(b.ToByteArray()).ToArray(); - byte[] hashedValue = algorithm.ComputeHash(concatenatedValue); - return FromByteArray(hashedValue, a.AlgorithmType); - } - - /// - /// Concatenates this hash with another hash. - /// - /// The other hash to concatenate with this hash. - /// Returns the concatenation of the two instances. - public Hash Concatenate(Hash other) - { - return Concatenate(this, other); - } - - /// - /// Concatenates this hash with another hash. - /// - /// The other hash to concatenate with this hash. - /// The length of the hash in bytes. - /// Returns the concatenation of the two instances. - public Hash Concatenate(Hash other, int length) - { - return Concatenate(this, other, length); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Concatenate.cs b/OnixLabs.Security.Cryptography/Hash.Concatenate.cs new file mode 100644 index 0000000..96f32bd --- /dev/null +++ b/OnixLabs.Security.Cryptography/Hash.Concatenate.cs @@ -0,0 +1,46 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Security.Cryptography; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +public readonly partial struct Hash +{ + /// + /// Concatenates the left-hand and right-hand hashes using the specified hash algorithm. + /// + /// The which will be used to compute the hash. + /// The left-hand hash to concatenate. + /// The right-hand hash to concatenate. + /// Returns a cryptographic hash representing the concatenation of the left-hand and right-hand hash values. + public static Hash Concatenate(HashAlgorithm algorithm, Hash left, Hash right) + { + byte[] data = left.ToByteArray().ConcatenateWith(right.ToByteArray()); + byte[] hash = algorithm.ComputeHash(data); + return new Hash(hash); + } + + /// + /// Concatenates the left-hand and right-hand hashes using the specified hash algorithm. + /// + /// The which will be used to compute the hash. + /// The other hash to concatenate with the current hash. + /// Returns a cryptographic hash representing the concatenation of the left-hand and right-hand hash values. + public Hash Concatenate(HashAlgorithm algorithm, Hash other) + { + return Concatenate(algorithm, this, other); + } +} diff --git a/OnixLabs.Security.Cryptography/Hash.Equatable.cs b/OnixLabs.Security.Cryptography/Hash.Equatable.cs index 14b77dc..85b449e 100644 --- a/OnixLabs.Security.Cryptography/Hash.Equatable.cs +++ b/OnixLabs.Security.Cryptography/Hash.Equatable.cs @@ -1,73 +1,72 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Linq; using OnixLabs.Core.Linq; namespace OnixLabs.Security.Cryptography; -public readonly partial struct Hash : IEquatable +public readonly partial struct Hash { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(Hash a, Hash b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(Hash other) { - return Equals(a, b); + return value.SequenceEqual(other.value); } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(Hash a, Hash b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return obj is Hash other && Equals(other); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public bool Equals(Hash other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return other.AlgorithmType == AlgorithmType && other.Value.SequenceEqual(Value); + return value.GetContentHashCode(); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(Hash left, Hash right) { - return obj is Hash other && Equals(other); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(Hash left, Hash right) { - return HashCode.Combine(AlgorithmType, Value.GetContentHashCode()); + return !Equals(left, right); } } diff --git a/OnixLabs.Security.Cryptography/Hash.From.cs b/OnixLabs.Security.Cryptography/Hash.From.cs deleted file mode 100644 index 7dec22c..0000000 --- a/OnixLabs.Security.Cryptography/Hash.From.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Creates a instance from a array. - /// This will create a hash of an unknown type. - /// - /// The array to represent as a hash. - /// A new instance. - public static Hash FromByteArray(byte[] value) - { - return FromByteArray(value, HashAlgorithmType.Unknown); - } - - /// - /// Creates a instance from a array. - /// - /// The array to represent as a hash. - /// The hash algorithm type of the hash. - /// A new instance. - public static Hash FromByteArray(byte[] value, HashAlgorithmType type) - { - return new Hash(value, type); - } - - /// - /// Creates a from the specified value. - /// - /// The Base-16 from which to construct a hash value. - /// Returns an from the specified Base-16 value. - public static Hash FromBase16(Base16 value) - { - byte[] bytes = value.ToByteArray(); - return FromByteArray(bytes); - } - - /// - /// Creates a from the specified value. - /// - /// The Base-32 from which to construct a hash value. - /// Returns an from the specified Base-32 value. - public static Hash FromBase32(Base32 value) - { - byte[] bytes = value.ToByteArray(); - return FromByteArray(bytes); - } - - /// - /// Creates a from the specified value. - /// - /// The Base-58 from which to construct a hash value. - /// Returns an from the specified Base-58 value. - public static Hash FromBase58(Base58 value) - { - byte[] bytes = value.ToByteArray(); - return FromByteArray(bytes); - } - - /// - /// Creates a from the specified value. - /// - /// The Base-64 from which to construct a hash value. - /// Returns an from the specified Base-64 value. - public static Hash FromBase64(Base64 value) - { - byte[] bytes = value.ToByteArray(); - return FromByteArray(bytes); - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.Parse.cs b/OnixLabs.Security.Cryptography/Hash.Parse.cs deleted file mode 100644 index 0806815..0000000 --- a/OnixLabs.Security.Cryptography/Hash.Parse.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using OnixLabs.Core; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hash -{ - /// - /// Parses a hexadecimal representation of a hash into a instance. - /// - /// A that contains a hash to convert. - /// The hash algorithm type of the hash. - /// A new instance. - public static Hash Parse(string value, HashAlgorithmType? type = null) - { - HashAlgorithmType parsedType = GetParsedHashAlgorithmType(value); - byte[] parsedValue = GetParsedHashValue(value); - - if (type is not null) - { - CheckMatchingHashAlgorithms(parsedType, type); - } - - return FromByteArray(parsedValue, parsedType); - } - - /// - /// Attempts to parse a hexadecimal representation of a hash into a instance. - /// - /// A that contains a hash to convert. - /// The hash algorithm type of the hash. - /// The result if conversion was successful. - /// Returns true if the hash conversion was successful; otherwise, false. - public static bool TryParse(string value, HashAlgorithmType? type, out Hash hash) - { - try - { - hash = Parse(value, type); - return true; - } - catch - { - hash = default; - return false; - } - } - - /// - /// Parses a from the specified value. - /// - /// The hash value to parse. - /// Returns a from the specified value. - private static HashAlgorithmType GetParsedHashAlgorithmType(string value) - { - string defaultHashAlgorithmType = HashAlgorithmType.Unknown.Name; - string parsedType = value.SubstringBefore(':', defaultHashAlgorithmType); - return HashAlgorithmType.FromName(parsedType); - } - - /// - /// Parses a array from the specified value. - /// - /// The hash value to parse. - /// Returns a array from the specified value. - private static byte[] GetParsedHashValue(string value) - { - string parsedValue = value.SubstringAfter(':'); - return Convert.FromHexString(parsedValue); - } - - /// - /// Checks that the parsed and specified instances match. - /// - /// The parsed to check. - /// The specified to check. - /// If the instances do not match. - private static void CheckMatchingHashAlgorithms(HashAlgorithmType parsed, HashAlgorithmType specified) - { - if (parsed != specified) - { - throw new InvalidOperationException( - $"The parsed hash algorithm type '{parsed}' does not match the expected hash algorithm type '{specified}'."); - } - } -} diff --git a/OnixLabs.Security.Cryptography/Hash.To.cs b/OnixLabs.Security.Cryptography/Hash.To.cs index f086e86..75d7395 100644 --- a/OnixLabs.Security.Cryptography/Hash.To.cs +++ b/OnixLabs.Security.Cryptography/Hash.To.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,92 +14,26 @@ using System; using OnixLabs.Core; -using OnixLabs.Core.Text; namespace OnixLabs.Security.Cryptography; public readonly partial struct Hash { /// - /// Returns a array containing the underlying hash data. + /// Gets the underlying representation of the current instance. /// - /// A array containing the underlying hash data. + /// Return the underlying representation of the current instance. public byte[] ToByteArray() { - return Value.Copy(); + return value.Copy(); } /// - /// Returns a value that represents the underlying hash data. + /// Returns a that represents the current object. /// - /// Returns a value that represents the underlying hash data. - public Base16 ToBase16() - { - return Base16.FromByteArray(Value); - } - - /// - /// Returns a value that represents the underlying hash data. - /// - /// Returns a value that represents the underlying hash data. - public Base32 ToBase32() - { - return ToBase32(Base32Alphabet.Default); - } - - /// - /// Returns a value that represents the underlying hash data. - /// - /// The Base-32 alphabet to use to encode the hash data. - /// Returns a value that represents the underlying hash data. - public Base32 ToBase32(Base32Alphabet alphabet) - { - return Base32.FromByteArray(Value, alphabet); - } - - /// - /// Returns a value that represents the underlying hash data. - /// - /// Returns a value that represents the underlying hash data. - public Base58 ToBase58() - { - return ToBase58(Base58Alphabet.Default); - } - - /// - /// Returns a value that represents the underlying hash data. - /// - /// The Base-58 alphabet to use to encode the hash data. - /// Returns a value that represents the underlying hash data. - public Base58 ToBase58(Base58Alphabet alphabet) - { - return Base58.FromByteArray(Value, alphabet); - } - - /// - /// Returns a value that represents the underlying hash data. - /// - /// Returns a value that represents the underlying hash data. - public Base64 ToBase64() - { - return Base64.FromByteArray(Value); - } - - /// - /// Returns a that represents the current object. - /// - /// A that represents the current object. + /// Returns that represents the current object. public override string ToString() { - return Convert.ToHexString(Value).ToLower(); - } - - /// - /// Returns a that represents the current object, including the hash algorithm type. - /// - /// A that represents the current object, including the hash algorithm type. - public string ToStringWithAlgorithmType() - { - return $"{AlgorithmType.Name}:{ToString()}"; + return Convert.ToHexString(value).ToLower(); } } diff --git a/OnixLabs.Security.Cryptography/Hash.cs b/OnixLabs.Security.Cryptography/Hash.cs index fe2903a..5bb1b3a 100644 --- a/OnixLabs.Security.Cryptography/Hash.cs +++ b/OnixLabs.Security.Cryptography/Hash.cs @@ -1,44 +1,37 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.Linq; +using OnixLabs.Core; + namespace OnixLabs.Security.Cryptography; /// /// Represents a cryptographic hash. /// -public readonly partial struct Hash +/// The underlying value of the cryptographic hash. +public readonly partial struct Hash(ReadOnlySpan value) : ICryptoPrimitive, IValueComparable { + private readonly byte[] value = value.ToArray(); + /// /// Initializes a new instance of the struct. /// - /// The underlying hexadecimal value of the hash. - /// The hash algorithm type of the hash. - private Hash(byte[] value, HashAlgorithmType type) + /// The underlying value of the cryptographic hash. + /// The length of the cryptographic hash in bytes. + public Hash(byte value, int length) : this(Enumerable.Repeat(value, length).ToArray()) { - type.VerifyHashLength(value); - - AlgorithmType = type; - Value = value; } - - /// - /// Gets the hash algorithm type of the hash. - /// - public HashAlgorithmType AlgorithmType { get; } - - /// - /// Gets the underlying value of the hash. - /// - private byte[] Value { get; } } diff --git a/OnixLabs.Security.Cryptography/HashAlgorithmType.Enumerations.cs b/OnixLabs.Security.Cryptography/HashAlgorithmType.Enumerations.cs deleted file mode 100644 index a359816..0000000 --- a/OnixLabs.Security.Cryptography/HashAlgorithmType.Enumerations.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class HashAlgorithmType -{ - /// - /// An unknown hash algorithm. - /// - public static readonly HashAlgorithmType Unknown = new(0, nameof(Unknown), UnknownLength, false); - - /// - /// The MD5 hash algorithm. - /// - public static readonly HashAlgorithmType Md5Hash = new(1, nameof(Md5Hash), 16, false); - - /// - /// The SHA-1 hash algorithm. - /// - public static readonly HashAlgorithmType Sha1Hash = new(2, nameof(Sha1Hash), 20, false); - - /// - /// The SHA-2 256-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha2Hash256 = new(3, nameof(Sha2Hash256), 32, false); - - /// - /// The SHA-2 384-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha2Hash384 = new(4, nameof(Sha2Hash384), 48, false); - - /// - /// The SHA-2 512-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha2Hash512 = new(5, nameof(Sha2Hash512), 64, false); - - /// - /// The SHA-3 224-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha3Hash224 = new(6, nameof(Sha3Hash224), 28, false); - - /// - /// The SHA-3 256-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha3Hash256 = new(7, nameof(Sha3Hash256), 32, false); - - /// - /// The SHA-3 384-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha3Hash384 = new(8, nameof(Sha3Hash384), 48, false); - - /// - /// The SHA-3 512-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha3Hash512 = new(9, nameof(Sha3Hash512), 64, false); - - /// - /// The SHA-3 Shake 128-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha3Shake128 = new(10, nameof(Sha3Shake128), UnknownLength, false); - - /// - /// The SHA-3 Shake 256-bit hash algorithm. - /// - public static readonly HashAlgorithmType Sha3Shake256 = new(11, nameof(Sha3Shake256), UnknownLength, false); - - /// - /// The MD5 HMAC keyed hash algorithm. - /// - public static readonly HashAlgorithmType Md5Hmac = new(12, nameof(Md5Hmac), 16, true); - - /// - /// The SHA-1 HMAC keyed hash algorithm. - /// - public static readonly HashAlgorithmType Sha1Hmac = new(13, nameof(Sha1Hmac), 20, true); - - /// - /// The SHA-2 256-bit HMAC keyed hash algorithm. - /// - public static readonly HashAlgorithmType Sha2Hmac256 = new(14, nameof(Sha2Hmac256), 32, true); - - /// - /// The SHA-2 384-bit HMAC keyed hash algorithm. - /// - public static readonly HashAlgorithmType Sha2Hmac384 = new(15, nameof(Sha2Hmac384), 48, true); - - /// - /// The SHA-2 512-bit HMAC keyed hash algorithm. - /// - public static readonly HashAlgorithmType Sha2Hmac512 = new(16, nameof(Sha2Hmac512), 64, true); -} diff --git a/OnixLabs.Security.Cryptography/HashAlgorithmType.Get.cs b/OnixLabs.Security.Cryptography/HashAlgorithmType.Get.cs deleted file mode 100644 index e8de590..0000000 --- a/OnixLabs.Security.Cryptography/HashAlgorithmType.Get.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Security.Cryptography; -using static OnixLabs.Core.Preconditions; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class HashAlgorithmType -{ - /// - /// Gets the hash algorithm for this instance. - /// - /// Returns a for this . - public HashAlgorithm GetHashAlgorithm() - { - return GetHashAlgorithm(Length); - } - - /// - /// Gets the hash algorithm for this instance. - /// - /// Specifies the expected output hash length. - /// Returns a for this . - /// If the hash algorithm does not expect an output length. - /// If the hash algorithm is unknown. - public HashAlgorithm GetHashAlgorithm(int length) - { - Check(IsUnknown || length == Length, $"Output length not expected for the specified hash algorithm: {Name}"); - - return Name switch - { - nameof(Md5Hash) => MD5.Create(), - nameof(Sha1Hash) => SHA1.Create(), - nameof(Sha2Hash256) => SHA256.Create(), - nameof(Sha2Hash384) => SHA384.Create(), - nameof(Sha2Hash512) => SHA512.Create(), - nameof(Sha3Hash224) => Sha3.CreateSha3Hash224(), - nameof(Sha3Hash256) => Sha3.CreateSha3Hash256(), - nameof(Sha3Hash384) => Sha3.CreateSha3Hash384(), - nameof(Sha3Hash512) => Sha3.CreateSha3Hash512(), - nameof(Sha3Shake128) => Sha3.CreateSha3Shake128(length), - nameof(Sha3Shake256) => Sha3.CreateSha3Shake256(length), - _ => throw new ArgumentException($"Hash algorithm '{Name}' is unknown.") - }; - } - - /// - /// Gets the keyed hash algorithm for this instance. - /// - /// The key that should be used by the keyed hash algorithm. - /// Returns a for this . - /// If the hash algorithm is unknown or is not a keyed hash algorithm. - public KeyedHashAlgorithm GetKeyedHashAlgorithm(byte[]? key = null) - { - Check(IsKeyBased, $"Hash algorithm type '{Name}' is not a keyed hash algorithm."); - - return Name switch - { - nameof(Md5Hmac) => key is null ? new HMACMD5() : new HMACMD5(key), - nameof(Sha1Hmac) => key is null ? new HMACSHA1() : new HMACSHA1(key), - nameof(Sha2Hmac256) => key is null ? new HMACSHA256() : new HMACSHA256(key), - nameof(Sha2Hmac384) => key is null ? new HMACSHA384() : new HMACSHA384(key), - nameof(Sha2Hmac512) => key is null ? new HMACSHA512() : new HMACSHA512(key), - _ => throw new ArgumentException($"Hash algorithm '{Name}' is unknown.") - }; - } - - /// - /// Gets the equivalent for this . - /// - /// Returns the equivalent for this . - /// If there is no corresponding equivalent for this . - public HashAlgorithmName GetHashAlgorithmName() - { - return Name switch - { - nameof(Md5Hash) => HashAlgorithmName.MD5, - nameof(Sha1Hash) => HashAlgorithmName.SHA1, - nameof(Sha2Hash256) => HashAlgorithmName.SHA256, - nameof(Sha2Hash384) => HashAlgorithmName.SHA384, - nameof(Sha2Hash512) => HashAlgorithmName.SHA512, - _ => throw new ArgumentException($"No corresponding {nameof(HashAlgorithmName)} for '{Name}'.") - }; - } -} diff --git a/OnixLabs.Security.Cryptography/HashAlgorithmType.Verify.cs b/OnixLabs.Security.Cryptography/HashAlgorithmType.Verify.cs deleted file mode 100644 index 21065a7..0000000 --- a/OnixLabs.Security.Cryptography/HashAlgorithmType.Verify.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using static OnixLabs.Core.Preconditions; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class HashAlgorithmType -{ - /// - /// Determines whether the length of a byte array is valid. - /// - /// The byte array to length check. - /// Returns true if the length of the byte array is valid; otherwise, false. - public bool IsValidHashLength(byte[] value) - { - return Length == -1 || Length == value.Length; - } - - /// - /// Verifies that the length of a byte array is valid. - /// - /// The byte array to length check. - /// If the length of the byte array is invalid. - public void VerifyHashLength(byte[] value) - { - Check(IsValidHashLength(value), "The length of the hash is invalid.", nameof(value)); - } -} diff --git a/OnixLabs.Security.Cryptography/HashAlgorithmType.cs b/OnixLabs.Security.Cryptography/HashAlgorithmType.cs deleted file mode 100644 index ada1def..0000000 --- a/OnixLabs.Security.Cryptography/HashAlgorithmType.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core; - -namespace OnixLabs.Security.Cryptography; - -/// -/// Specifies values that define known hash algorithm types. -/// -public sealed partial class HashAlgorithmType : Enumeration -{ - /// - /// A constant that defines an unknown hash algorithm length. - /// - private const int UnknownLength = -1; - - /// - /// Initializes a new instance of the class. - /// - /// The value of the hash algorithm type. - /// The name of the hash algorithm type. - /// The length in bytes of the hash algorithm type. - /// Determines whether the algorithm is a key based hash algorithm. - private HashAlgorithmType(int value, string name, int length, bool isKeyBased) : base(value, name) - { - Length = length; - IsKeyBased = isKeyBased; - } - - /// - /// Gets the length of an algorithm's hash in bytes. - /// -1 Means that the algorithm's hash is of variable length, or is unknown. - /// - public int Length { get; } - - /// - /// Gets a value that determines whether the algorithm is a key based hash algorithm. - /// - public bool IsKeyBased { get; } - - /// - /// Gets a value that determines whether the algorithm type is unknown. - /// - public bool IsUnknown => ReferenceEquals(this, Unknown); -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Compute.Md5Hmac.cs b/OnixLabs.Security.Cryptography/Hmac.Compute.Md5Hmac.cs deleted file mode 100644 index d1cf46f..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Compute.Md5Hmac.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes an MD5 HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeMd5Hmac(string value, string key) - { - return ComputeMd5Hmac(value, key, Encoding.Default); - } - - /// - /// Computes an MD5 HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static Hmac ComputeMd5Hmac(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return ComputeMd5Hmac(valueBytes, keyBytes); - } - - /// - /// Computes an MD5 HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeMd5Hmac(byte[] value, byte[] key) - { - return ComputeHmac(value, key, HashAlgorithmType.Md5Hmac); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Compute.Sha1Hmac.cs b/OnixLabs.Security.Cryptography/Hmac.Compute.Sha1Hmac.cs deleted file mode 100644 index 5c63312..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Compute.Sha1Hmac.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a SHA-1 HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeSha1Hmac(string value, string key) - { - return ComputeSha1Hmac(value, key, Encoding.Default); - } - - /// - /// Computes a SHA-1 HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static Hmac ComputeSha1Hmac(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return ComputeSha1Hmac(valueBytes, keyBytes); - } - - /// - /// Computes a SHA-1 HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeSha1Hmac(byte[] value, byte[] key) - { - return ComputeHmac(value, key, HashAlgorithmType.Sha1Hmac); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac256.cs b/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac256.cs deleted file mode 100644 index d936676..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac256.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a SHA-2 256-bit HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac256(string value, string key) - { - return ComputeSha2Hmac256(value, key, Encoding.Default); - } - - /// - /// Computes a SHA-2 256-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac256(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return ComputeSha2Hmac256(valueBytes, keyBytes); - } - - /// - /// Computes a SHA-2 256-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac256(byte[] value, byte[] key) - { - return ComputeHmac(value, key, HashAlgorithmType.Sha2Hmac256); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac384.cs b/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac384.cs deleted file mode 100644 index c05966d..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac384.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a SHA-2 384-bit HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac384(string value, string key) - { - return ComputeSha2Hmac384(value, key, Encoding.Default); - } - - /// - /// Computes a SHA-2 384-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac384(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return ComputeSha2Hmac384(valueBytes, keyBytes); - } - - /// - /// Computes a SHA-2 384-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac384(byte[] value, byte[] key) - { - return ComputeHmac(value, key, HashAlgorithmType.Sha2Hmac384); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac512.cs b/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac512.cs deleted file mode 100644 index b390832..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Compute.Sha2Hmac512.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a SHA-2 512-bit HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac512(string value, string key) - { - return ComputeSha2Hmac512(value, key, Encoding.Default); - } - - /// - /// Computes a SHA-2 512-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac512(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return ComputeSha2Hmac512(valueBytes, keyBytes); - } - - /// - /// Computes a SHA-2 512-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static Hmac ComputeSha2Hmac512(byte[] value, byte[] key) - { - return ComputeHmac(value, key, HashAlgorithmType.Sha2Hmac512); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Compute.cs b/OnixLabs.Security.Cryptography/Hmac.Compute.cs deleted file mode 100644 index 1fc842f..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Compute.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Security.Cryptography; -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a hashed message authentication code (HMAC). - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The hash algorithm type of the computed HMAC. - /// Returns a representing the specified value and key. - public static Hmac ComputeHmac(string value, string key, HashAlgorithmType type) - { - return ComputeHmac(value, key, type, Encoding.Default); - } - - /// - /// Computes a hashed message authentication code (HMAC). - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The hash algorithm type of the computed HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a representing the specified value and key. - public static Hmac ComputeHmac(string value, string key, HashAlgorithmType type, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return ComputeHmac(valueBytes, keyBytes, type); - } - - /// - /// Computes a hashed message authentication code (HMAC). - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The hash algorithm type of the computed HMAC. - /// Returns a representing the specified value and key. - public static Hmac ComputeHmac(byte[] value, byte[] key, HashAlgorithmType type) - { - using KeyedHashAlgorithm algorithm = type.GetKeyedHashAlgorithm(key); - - byte[] data = algorithm.ComputeHash(value); - Hash hash = Hash.FromByteArray(data, type); - - return Create(hash, value); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Md5Hmac.cs b/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Md5Hmac.cs deleted file mode 100644 index e246790..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Md5Hmac.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes an MD5 HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeMd5HmacAsync(string value, string key) - { - return await ComputeMd5HmacAsync(value, key, Encoding.Default); - } - - /// - /// Computes an MD5 HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static async Task ComputeMd5HmacAsync(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return await ComputeMd5HmacAsync(valueBytes, keyBytes); - } - - /// - /// Computes an MD5 HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeMd5HmacAsync(byte[] value, byte[] key) - { - return await ComputeHmacAsync(value, key, HashAlgorithmType.Md5Hmac); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha1Hmac.cs b/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha1Hmac.cs deleted file mode 100644 index 46fb8d0..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha1Hmac.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a SHA-1 HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeSha1HmacAsync(string value, string key) - { - return await ComputeSha1HmacAsync(value, key, Encoding.Default); - } - - /// - /// Computes a SHA-1 HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static async Task ComputeSha1HmacAsync(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return await ComputeSha1HmacAsync(valueBytes, keyBytes); - } - - /// - /// Computes a SHA-1 HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeSha1HmacAsync(byte[] value, byte[] key) - { - return await ComputeHmacAsync(value, key, HashAlgorithmType.Sha1Hmac); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac256.cs b/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac256.cs deleted file mode 100644 index 43bf383..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac256.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a SHA-2 256-bit HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac256Async(string value, string key) - { - return await ComputeSha2Hmac256Async(value, key, Encoding.Default); - } - - /// - /// Computes a SHA-2 256-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac256Async(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return await ComputeSha2Hmac256Async(valueBytes, keyBytes); - } - - /// - /// Computes a SHA-2 256-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac256Async(byte[] value, byte[] key) - { - return await ComputeHmacAsync(value, key, HashAlgorithmType.Sha2Hmac256); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac384.cs b/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac384.cs deleted file mode 100644 index 1bf5ad2..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac384.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a SHA-2 384-bit HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac384Async(string value, string key) - { - return await ComputeSha2Hmac384Async(value, key, Encoding.Default); - } - - /// - /// Computes a SHA-2 384-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac384Async(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return await ComputeSha2Hmac384Async(valueBytes, keyBytes); - } - - /// - /// Computes a SHA-2 384-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac384Async(byte[] value, byte[] key) - { - return await ComputeHmacAsync(value, key, HashAlgorithmType.Sha2Hmac384); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac512.cs b/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac512.cs deleted file mode 100644 index c2cc2ff..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.Sha2Hmac512.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a SHA-2 512-bit HMAC from the specified value and key. - /// This will use the default encoding to convert the input value and key into a byte array. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac512Async(string value, string key) - { - return await ComputeSha2Hmac512Async(value, key, Encoding.Default); - } - - /// - /// Computes a SHA-2 512-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac512Async(string value, string key, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return await ComputeSha2Hmac512Async(valueBytes, keyBytes); - } - - /// - /// Computes a SHA-2 512-bit HMAC from the specified value and key. - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// Returns a of the input value and key. - public static async Task ComputeSha2Hmac512Async(byte[] value, byte[] key) - { - return await ComputeHmacAsync(value, key, HashAlgorithmType.Sha2Hmac512); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.cs b/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.cs deleted file mode 100644 index f09bf8b..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.ComputeAsync.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.IO; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Computes a hashed message authentication code (HMAC). - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The hash algorithm type of the computed HMAC. - /// Returns a representing the specified value and key. - public static async Task ComputeHmacAsync(string value, string key, HashAlgorithmType type) - { - return await ComputeHmacAsync(value, key, type, Encoding.Default); - } - - /// - /// Computes a hashed message authentication code (HMAC). - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The hash algorithm type of the computed HMAC. - /// The encoding which will be used to convert the value and key into a byte array. - /// Returns a representing the specified value and key. - public static async Task ComputeHmacAsync(string value, string key, HashAlgorithmType type, Encoding encoding) - { - byte[] valueBytes = encoding.GetBytes(value); - byte[] keyBytes = encoding.GetBytes(key); - - return await ComputeHmacAsync(valueBytes, keyBytes, type); - } - - /// - /// Computes a hashed message authentication code (HMAC). - /// - /// The value for which to compute a HMAC. - /// The key for which to compute a HMAC. - /// The hash algorithm type of the computed HMAC. - /// Returns a representing the specified value and key. - public static async Task ComputeHmacAsync(byte[] value, byte[] key, HashAlgorithmType type) - { - using KeyedHashAlgorithm algorithm = type.GetKeyedHashAlgorithm(key); - await using Stream stream = new MemoryStream(value); - - byte[] data = await algorithm.ComputeHashAsync(stream); - Hash hash = Hash.FromByteArray(data, type); - - return Create(hash, value); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Create.cs b/OnixLabs.Security.Cryptography/Hmac.Create.cs deleted file mode 100644 index 2ddc8ca..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Create.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Creates a hashed message authentication code (HMAC). - /// - /// The representing the HMAC. - /// The underlying un-hashed data. - /// Returns a new instance. - public static Hmac Create(Hash hash, byte[] data) - { - return new Hmac(hash, data); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Equatable.cs b/OnixLabs.Security.Cryptography/Hmac.Equatable.cs deleted file mode 100644 index c418ded..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Equatable.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Linq; -using OnixLabs.Core.Linq; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac : IEquatable -{ - /// - /// Performs an equality check between two object instances. - /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(Hmac a, Hmac b) - { - return Equals(a, b); - } - - /// - /// Performs an inequality check between two object instances. - /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(Hmac a, Hmac b) - { - return !Equals(a, b); - } - - /// - /// Checks for equality between this instance and another object. - /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public bool Equals(Hmac other) - { - return other.Hash == Hash && other.Data.SequenceEqual(Data); - } - - /// - /// Checks for equality between this instance and another object. - /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) - { - return obj is Hmac other && Equals(other); - } - - /// - /// Serves as a hash code function for this instance. - /// - /// A hash code for this instance. - public override int GetHashCode() - { - return HashCode.Combine(Hash, Data.GetContentHashCode()); - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Parse.cs b/OnixLabs.Security.Cryptography/Hmac.Parse.cs deleted file mode 100644 index d8633b3..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Parse.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using OnixLabs.Core; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Parses a hexadecimal representation of a HMAC into a instance. - /// - /// A that contains a HMAC to convert. - /// The hash algorithm type of the HMAC. - /// A new instance. - public static Hmac Parse(string value, HashAlgorithmType? type = null) - { - string hashComponent = value.SubstringBeforeLast(':'); - string dataComponent = value.SubstringAfterLast(':'); - - Hash hash = Hash.Parse(hashComponent, type); - byte[] data = Convert.FromHexString(dataComponent); - - return Create(hash, data); - } - - /// - /// Attempts to parse a hexadecimal representation of a HMAC into a instance. - /// - /// A that contains a HMAC to convert. - /// The hash algorithm type of the hash. - /// The result if conversion was successful. - /// Returns true if the hash conversion was successful; otherwise, false. - public static bool TryParse(string value, HashAlgorithmType? type, out Hmac hmac) - { - try - { - hmac = Parse(value, type); - return true; - } - catch - { - hmac = default; - return false; - } - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.Verify.cs b/OnixLabs.Security.Cryptography/Hmac.Verify.cs deleted file mode 100644 index 63161ac..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.Verify.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Security.Cryptography; -using System.Text; - -namespace OnixLabs.Security.Cryptography; - -public readonly partial struct Hmac -{ - /// - /// Determines whether the hashed message authentication code (HMAC) was created with the specified key. - /// - /// The key to validate against this . - /// Returns true if this was created with the specified key; otherwise, false. - public bool IsValid(string key) - { - return IsValid(key, Encoding.Default); - } - - /// - /// Determines whether the hashed message authentication code (HMAC) was created with the specified key. - /// - /// The key to validate against this . - /// The encoding which will be used to convert the key into a byte array. - /// Returns true if this was created with the specified key; otherwise, false. - public bool IsValid(string key, Encoding encoding) - { - byte[] keyBytes = encoding.GetBytes(key); - return IsValid(keyBytes); - } - - /// - /// Determines whether the hashed message authentication code (HMAC) was created with the specified key. - /// - /// The key to validate against this . - /// Returns true if this was created with the specified key; otherwise, false. - public bool IsValid(byte[] key) - { - return this == ComputeHmac(Data, key, Hash.AlgorithmType); - } - - /// - /// Verifies that the hashed message authentication code (HMAC) was created with the specified key. - /// - /// The key to validate against this . - public void Verify(string key) - { - Verify(key, Encoding.Default); - } - - /// - /// Verifies that the hashed message authentication code (HMAC) was created with the specified key. - /// - /// The encoding which will be used to convert the key into a byte array. - /// The key to validate against this . - public void Verify(string key, Encoding encoding) - { - byte[] keyBytes = encoding.GetBytes(key); - Verify(keyBytes); - } - - /// - /// Verifies that the hashed message authentication code (HMAC) was created with the specified key. - /// - /// The key to validate against this . - public void Verify(byte[] key) - { - if (!IsValid(key)) - { - throw new CryptographicException("The HMAC was could not be verified with the specified key."); - } - } -} diff --git a/OnixLabs.Security.Cryptography/Hmac.cs b/OnixLabs.Security.Cryptography/Hmac.cs deleted file mode 100644 index 019cbf6..0000000 --- a/OnixLabs.Security.Cryptography/Hmac.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OnixLabs.Core; - -namespace OnixLabs.Security.Cryptography; - -/// -/// Represents a hashed message authentication code (HMAC). -/// -public readonly partial struct Hmac -{ - /// - /// Represents the underlying un-hashed data. - /// - private readonly byte[] data; - - /// - /// Initializes a new instance of the struct. - /// - /// The representing the HMAC. - /// The underlying un-hashed data. - private Hmac(Hash hash, byte[] data) - { - Hash = hash; - this.data = data; - } - - /// - /// Gets the representing the HMAC. - /// - public Hash Hash { get; } - - /// - /// Gets the underlying un-hashed data. - /// - public byte[] Data => data.Copy(); -} diff --git a/OnixLabs.Security.Cryptography/ICryptoPrimitive.cs b/OnixLabs.Security.Cryptography/ICryptoPrimitive.cs new file mode 100644 index 0000000..3dec14f --- /dev/null +++ b/OnixLabs.Security.Cryptography/ICryptoPrimitive.cs @@ -0,0 +1,23 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +/// +/// Defines a cryptographic primitive. +/// +/// The underlying type of the cryptographic primitive. +public interface ICryptoPrimitive : IValueEquatable, IBinaryConvertible where T : ICryptoPrimitive; diff --git a/OnixLabs.Security.Cryptography/IEcdsaPrivateKey.cs b/OnixLabs.Security.Cryptography/IEcdsaPrivateKey.cs new file mode 100644 index 0000000..de31a28 --- /dev/null +++ b/OnixLabs.Security.Cryptography/IEcdsaPrivateKey.cs @@ -0,0 +1,170 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Security.Cryptography; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +/// +/// Defines an ECDSA cryptographic private key. +/// +public interface IEcdsaPrivateKey : IBinaryConvertible +{ + /// + /// Imports the ECDSA cryptographic private key data in PKCS #8 format. + /// + /// The cryptographic private key data to import. + /// Returns a new instance from the imported cryptographic private key data. + public static abstract EcdsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data); + + /// + /// Imports the ECDSA cryptographic private key data in PKCS #8 format. + /// + /// The cryptographic private key data to import. + /// The number of bytes read from the input data. + /// Returns a new instance from the imported cryptographic private key data. + public static abstract EcdsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, out int bytesRead); + + /// + /// Imports the ECDSA cryptographic private key data in encrypted PKCS #8 format. + /// + /// The cryptographic private key data to import. + /// The password required for password based decryption. + /// Returns a new instance from the imported cryptographic private key data. + public static abstract EcdsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, ReadOnlySpan password); + + /// + /// Imports the ECDSA cryptographic private key data in encrypted PKCS #8 format. + /// + /// The cryptographic private key data to import. + /// The password required for password based decryption. + /// The number of bytes read from the input data. + /// Returns a new instance from the imported cryptographic private key data. + public static abstract EcdsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, ReadOnlySpan password, out int bytesRead); + + /// + /// Exports the ECDSA cryptographic private key data in PKCS #8 format. + /// + /// Returns a new instance containing the ECDSA cryptographic private key data in PKCS #8 format. + byte[] ExportPkcs8PrivateKey(); + + /// + /// Exports the ECDSA cryptographic private key data in encrypted PKCS #8 format. + /// + /// The password to use for encryption. + /// The parameters required for password based encryption. + /// Returns a new instance containing the ECDSA cryptographic private key data in PKCS #8 format. + byte[] ExportPkcs8PrivateKey(ReadOnlySpan password, PbeParameters parameters); + + /// + /// Gets the ECDSA cryptographic public key component from the current ECDSA cryptographic private key. + /// + /// Returns a new instance containing the ECDSA cryptographic public key component from the current ECDSA cryptographic private key. + EcdsaPublicKey GetPublicKey(); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Signs the specified . + /// + /// The hash to sign. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignHash(Hash hash, DSASignatureFormat format = default); + + /// + /// Signs the specified . + /// + /// The hash to sign. + /// The digital signature format which will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignHash(ReadOnlySpan hash, DSASignatureFormat format = default); +} diff --git a/OnixLabs.Security.Cryptography/IEcdsaPublicKey.cs b/OnixLabs.Security.Cryptography/IEcdsaPublicKey.cs new file mode 100644 index 0000000..d944840 --- /dev/null +++ b/OnixLabs.Security.Cryptography/IEcdsaPublicKey.cs @@ -0,0 +1,514 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Security.Cryptography; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +/// +/// Defines an ECDSA cryptographic public key. +/// +public interface IEcdsaPublicKey : IBinaryConvertible +{ + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Determines whether the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + bool IsHashValid(ReadOnlySpan signature, Hash hash, DSASignatureFormat format = default); + + /// + /// Determines whether the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + bool IsHashValid(ReadOnlySpan signature, ReadOnlySpan hash, DSASignatureFormat format = default); + + /// + /// Determines whether the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + bool IsHashValid(DigitalSignature signature, Hash hash, DSASignatureFormat format = default); + + /// + /// Determines whether the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the ECDSA cryptographic private key that corresponds to the + /// current ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + bool IsHashValid(DigitalSignature signature, ReadOnlySpan hash, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, ReadOnlySpan data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, Stream data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, IBinaryConvertible data, HashAlgorithm algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, ReadOnlySpan data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, Stream data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified data was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned data to validate against the current ECDSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, IBinaryConvertible data, HashAlgorithmName algorithm, DSASignatureFormat format = default); + + /// + /// Verifies that the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyHash(ReadOnlySpan signature, Hash hash, DSASignatureFormat format = default); + + /// + /// Verifies that the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyHash(ReadOnlySpan signature, ReadOnlySpan hash, DSASignatureFormat format = default); + + /// + /// Verifies that the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyHash(DigitalSignature signature, Hash hash, DSASignatureFormat format = default); + + /// + /// Verifies that the specified hash was signed by the ECDSA cryptographic private key that corresponds to the current + /// ECDSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current ECDSA cryptographic public key. + /// The unsigned hash to validate against the current ECDSA cryptographic public key. + /// The digital signature format that was used to generate the cryptographic digital signature. + void VerifyHash(DigitalSignature signature, ReadOnlySpan hash, DSASignatureFormat format = default); +} diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.Empty.cs b/OnixLabs.Security.Cryptography/IHashable.cs similarity index 60% rename from OnixLabs.Security.Cryptography/DigitalSignature.Empty.cs rename to OnixLabs.Security.Cryptography/IHashable.cs index f64bb8b..a902856 100644 --- a/OnixLabs.Security.Cryptography/DigitalSignature.Empty.cs +++ b/OnixLabs.Security.Cryptography/IHashable.cs @@ -1,33 +1,29 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; +using System.Security.Cryptography; namespace OnixLabs.Security.Cryptography; -public readonly partial struct DigitalSignature +/// +/// Defines a mechanism for computing the of a data structure. +/// +public interface IHashable { /// - /// Gets an empty digital signature value. + /// Computes the hash of the current object. /// - public static readonly DigitalSignature Empty; - - /// - /// Initializes static members of the class. - /// - static DigitalSignature() - { - Empty = FromByteArray(Array.Empty()); - } + /// Returns the computed hash of the current object. + Hash ComputeHash(HashAlgorithm algorithm); } diff --git a/OnixLabs.Security.Cryptography/IRsaPrivateKey.cs b/OnixLabs.Security.Cryptography/IRsaPrivateKey.cs new file mode 100644 index 0000000..476aa0b --- /dev/null +++ b/OnixLabs.Security.Cryptography/IRsaPrivateKey.cs @@ -0,0 +1,134 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Security.Cryptography; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +/// +/// Defines an RSA cryptographic private key. +/// +public interface IRsaPrivateKey : IBinaryConvertible +{ + /// + /// Imports the RSA cryptographic private key data in PKCS #8 format. + /// + /// The cryptographic private key data to import. + /// Returns a new instance from the imported cryptographic private key data. + public static abstract RsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data); + + /// + /// Imports the RSA cryptographic private key data in PKCS #8 format. + /// + /// The cryptographic private key data to import. + /// The number of bytes read from the input data. + /// Returns a new instance from the imported cryptographic private key data. + public static abstract RsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, out int bytesRead); + + /// + /// Imports the RSA cryptographic private key data in encrypted PKCS #8 format. + /// + /// The cryptographic private key data to import. + /// The password required for password based decryption. + /// Returns a new instance from the imported cryptographic private key data. + public static abstract RsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, ReadOnlySpan password); + + /// + /// Imports the RSA cryptographic private key data in encrypted PKCS #8 format. + /// + /// The cryptographic private key data to import. + /// The password required for password based decryption. + /// The number of bytes read from the input data. + /// Returns a new instance from the imported cryptographic private key data. + public static abstract RsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, ReadOnlySpan password, out int bytesRead); + + /// + /// Exports the RSA cryptographic private key data in PKCS #8 format. + /// + /// Returns a new instance containing the RSA cryptographic private key data in PKCS #8 format. + byte[] ExportPkcs8PrivateKey(); + + /// + /// Exports the RSA cryptographic private key data in encrypted PKCS #8 format. + /// + /// The password to use for encryption. + /// The parameters required for password based encryption. + /// Returns a new instance containing the RSA cryptographic private key data in PKCS #8 format. + byte[] ExportPkcs8PrivateKey(ReadOnlySpan password, PbeParameters parameters); + + /// + /// Gets the RSA cryptographic public key component from the current RSA cryptographic private key. + /// + /// Returns a new instance containing the RSA cryptographic public key component from the current RSA cryptographic private key. + RsaPublicKey GetPublicKey(); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that will be used to hash the input data. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignData(IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Signs the specified . + /// + /// The hash to sign. + /// The hash algorithm that will be used to hash the input hash. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignHash(Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Signs the specified . + /// + /// The hash to sign. + /// The hash algorithm that will be used to hash the input hash. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + byte[] SignHash(ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding); +} diff --git a/OnixLabs.Security.Cryptography/IRsaPublicKey.cs b/OnixLabs.Security.Cryptography/IRsaPublicKey.cs new file mode 100644 index 0000000..81c75ae --- /dev/null +++ b/OnixLabs.Security.Cryptography/IRsaPublicKey.cs @@ -0,0 +1,322 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Security.Cryptography; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +/// +/// Defines an RSA cryptographic public key. +/// +public interface IRsaPublicKey : IBinaryConvertible +{ + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + bool IsDataValid(DigitalSignature signature, IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + bool IsHashValid(ReadOnlySpan signature, ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + bool IsHashValid(DigitalSignature signature, ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + bool IsHashValid(ReadOnlySpan signature, Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Determines whether the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + bool IsHashValid(DigitalSignature signature, Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyData(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyData(DigitalSignature signature, IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyHash(ReadOnlySpan signature, ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyHash(DigitalSignature signature, ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyHash(ReadOnlySpan signature, Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding); + + /// + /// Verifies that the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + void VerifyHash(DigitalSignature signature, Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding); +} diff --git a/OnixLabs.Security.Cryptography/KeyPair.Create.cs b/OnixLabs.Security.Cryptography/KeyPair.Create.cs deleted file mode 100644 index d34348a..0000000 --- a/OnixLabs.Security.Cryptography/KeyPair.Create.cs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Security.Cryptography; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class KeyPair -{ - /// - /// Creates an ECDSA public/private key pair. - /// - /// The for computing signature data. - /// Returns an ECDSA public/private key pair. - public static KeyPair CreateEcdsaKeyPair(HashAlgorithmType type) - { - using ECDsa asymmetricAlgorithm = ECDsa.Create(); - - PrivateKey privateKey = new EcdsaPrivateKey(asymmetricAlgorithm.ExportECPrivateKey(), type); - PublicKey publicKey = new EcdsaPublicKey(asymmetricAlgorithm.ExportSubjectPublicKeyInfo(), type); - - return new KeyPair(privateKey, publicKey, type); - } - - /// - /// Creates an ECDSA public/private key pair. - /// - /// The for computing signature data. - /// The curve to use for key generation. - /// Returns an ECDSA public/private key pair. - public static KeyPair CreateEcdsaKeyPair(HashAlgorithmType type, ECCurve curve) - { - using ECDsa asymmetricAlgorithm = ECDsa.Create(curve); - - PrivateKey privateKey = new EcdsaPrivateKey(asymmetricAlgorithm.ExportECPrivateKey(), type); - PublicKey publicKey = new EcdsaPublicKey(asymmetricAlgorithm.ExportSubjectPublicKeyInfo(), type); - - return new KeyPair(privateKey, publicKey, type); - } - - /// - /// Creates an ECDSA public/private key pair. - /// - /// The for computing signature data. - /// The parameters representing the key to use. - /// Returns an ECDSA public/private key pair. - public static KeyPair CreateEcdsaKeyPair(HashAlgorithmType type, ECParameters parameters) - { - using ECDsa asymmetricAlgorithm = ECDsa.Create(parameters); - - PrivateKey privateKey = new EcdsaPrivateKey(asymmetricAlgorithm.ExportECPrivateKey(), type); - PublicKey publicKey = new EcdsaPublicKey(asymmetricAlgorithm.ExportSubjectPublicKeyInfo(), type); - - return new KeyPair(privateKey, publicKey, type); - } - - /// - /// Creates an RSA public/private key pair. - /// - /// The for computing signature data. - /// The for computing signature data. - /// Returns an ECDSA public/private key pair. - public static KeyPair CreateRsaKeyPair(HashAlgorithmType type, RSASignaturePadding padding) - { - using RSA asymmetricAlgorithm = RSA.Create(); - - PrivateKey privateKey = new RsaPrivateKey(asymmetricAlgorithm.ExportRSAPrivateKey(), type, padding); - PublicKey publicKey = new RsaPublicKey(asymmetricAlgorithm.ExportRSAPublicKey(), type, padding); - - return new KeyPair(privateKey, publicKey, type); - } - - /// - /// Creates an RSA public/private key pair. - /// - /// The for computing signature data. - /// The for computing signature data. - /// The key size, in bits. - /// Returns an ECDSA public/private key pair. - public static KeyPair CreateRsaKeyPair(HashAlgorithmType type, RSASignaturePadding padding, int keySizeInBits) - { - using RSA asymmetricAlgorithm = RSA.Create(keySizeInBits); - - PrivateKey privateKey = new RsaPrivateKey(asymmetricAlgorithm.ExportRSAPrivateKey(), type, padding); - PublicKey publicKey = new RsaPublicKey(asymmetricAlgorithm.ExportRSAPublicKey(), type, padding); - - return new KeyPair(privateKey, publicKey, type); - } - - /// - /// Creates an RSA public/private key pair. - /// - /// The for computing signature data. - /// The for computing signature data. - /// The parameters for the RSA algorithm. - /// Returns an ECDSA public/private key pair. - public static KeyPair CreateRsaKeyPair(HashAlgorithmType type, RSASignaturePadding padding, RSAParameters parameters) - { - using RSA asymmetricAlgorithm = RSA.Create(parameters); - - PrivateKey privateKey = new RsaPrivateKey(asymmetricAlgorithm.ExportRSAPrivateKey(), type, padding); - PublicKey publicKey = new RsaPublicKey(asymmetricAlgorithm.ExportRSAPublicKey(), type, padding); - - return new KeyPair(privateKey, publicKey, type); - } -} diff --git a/OnixLabs.Security.Cryptography/KeyPair.Equatable.cs b/OnixLabs.Security.Cryptography/KeyPair.Equatable.cs deleted file mode 100644 index 51b2248..0000000 --- a/OnixLabs.Security.Cryptography/KeyPair.Equatable.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class KeyPair : IEquatable -{ - /// - /// Performs an equality check between two object instances. - /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(KeyPair a, KeyPair b) - { - return Equals(a, b); - } - - /// - /// Performs an inequality check between two object instances. - /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(KeyPair a, KeyPair b) - { - return !Equals(a, b); - } - - /// - /// Checks for equality between this instance and another object. - /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public bool Equals(KeyPair? other) - { - return ReferenceEquals(this, other) - || other is not null - && other.PrivateKey == PrivateKey - && other.PublicKey == PublicKey; - } - - /// - /// Checks for equality between this instance and another object. - /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) - { - return Equals(obj as KeyPair); - } - - /// - /// Serves as a hash code function for this instance. - /// - /// A hash code for this instance. - public override int GetHashCode() - { - return HashCode.Combine(PrivateKey, PublicKey); - } -} diff --git a/OnixLabs.Security.Cryptography/KeyPair.From.cs b/OnixLabs.Security.Cryptography/KeyPair.From.cs deleted file mode 100644 index 5120d94..0000000 --- a/OnixLabs.Security.Cryptography/KeyPair.From.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using static OnixLabs.Core.Preconditions; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class KeyPair -{ - /// - /// Creates a key pair from the specified public and private key components. - /// - /// The private key component of the key pair. - /// The public key component of the key pair. - /// The hash algorithm type of the key pair. - /// Returns a new instance from the key components. - /// If the private key hash algorithm type is mismatched. - /// If the public key hash algorithm type is mismatched. - /// If the private and public key components are mismatched. - public static KeyPair FromKeyComponents(PrivateKey privateKey, PublicKey publicKey, HashAlgorithmType type) - { - Check(privateKey.AlgorithmType == type, $"Private key hash algorithm type is mismatched with '{type}'.", nameof(privateKey)); - Check(publicKey.AlgorithmType == type, $"Public key hash algorithm type is mismatched with '{type}'.", nameof(publicKey)); - - byte[] random = Guid.NewGuid().ToByteArray(); - Hash hash = Hash.ComputeSha2Hash256(random); - DigitalSignature signature = privateKey.SignHash(hash); - - Check(signature.IsHashValid(hash, publicKey), "Invalid key pair. The specified public and private keys are mismatched."); - - return new KeyPair(privateKey, publicKey, type); - } -} diff --git a/OnixLabs.Security.Cryptography/KeyPair.cs b/OnixLabs.Security.Cryptography/KeyPair.cs deleted file mode 100644 index 420b2fb..0000000 --- a/OnixLabs.Security.Cryptography/KeyPair.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Security.Cryptography; - -/// -/// Represents a cryptographic public/private key pair. -/// -public sealed partial class KeyPair -{ - /// - /// Prevents new instances of the class from being created. - /// - /// The private key component of this key pair. - /// The public key component of this key pair. - /// The hash algorithm type of this key pair. - private KeyPair(PrivateKey privateKey, PublicKey publicKey, HashAlgorithmType algorithmType) - { - PrivateKey = privateKey; - PublicKey = publicKey; - AlgorithmType = algorithmType; - } - - /// - /// Gets the private key component of this key pair. - /// - public PrivateKey PrivateKey { get; } - - /// - /// Gets the public key component of this key pair. - /// - public PublicKey PublicKey { get; } - - /// - /// Gets the hash algorithm type of this key pair. - /// - public HashAlgorithmType AlgorithmType { get; } -} diff --git a/OnixLabs.Security.Cryptography/MerkleTree.Create.cs b/OnixLabs.Security.Cryptography/MerkleTree.Create.cs new file mode 100644 index 0000000..9e339eb --- /dev/null +++ b/OnixLabs.Security.Cryptography/MerkleTree.Create.cs @@ -0,0 +1,73 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using OnixLabs.Core.Linq; + +namespace OnixLabs.Security.Cryptography; + +public abstract partial class MerkleTree +{ + /// + /// Creates a Merkle tree from the specified leaf nodes. + /// + /// The Merkle tree leaf nodes from which to build a Merkle tree. + /// The hash algorithm that will be used to hash together left-hand and right-hand nodes. + /// Returns a new node that represents the Merkle root. + public static MerkleTree Create(IEnumerable leaves, HashAlgorithm algorithm) + { + IReadOnlyList nodes = leaves.Select(leaf => new MerkleTreeLeafNode(leaf)).ToList(); + Require(nodes.IsNotEmpty(), "Cannot construct a Merkle tree from an empty list.", nameof(leaves)); + return BuildMerkleTree(nodes, algorithm); + } + + /// + /// Creates a Merkle tree from the specified leaf nodes. + /// + /// The Merkle tree leaf nodes from which to build a Merkle tree. + /// The hash algorithm that will be used to hash together left-hand and right-hand nodes. + /// Returns a new node that represents the Merkle root. + public static MerkleTree Create(IEnumerable leaves, HashAlgorithm algorithm) where T : IHashable + { + return MerkleTree.Create(leaves, algorithm); + } + + /// + /// Builds a Merkle tree from the specified nodes. + /// + /// The Merkle tree nodes from which to build a Merkle tree. + /// The hash algorithm that will be used to hash together left-hand and right-hand nodes. + /// Returns a new node that represents the Merkle root. + private static MerkleTree BuildMerkleTree(IReadOnlyList nodes, HashAlgorithm algorithm) + { + while (true) + { + if (nodes.IsSingle()) return nodes.Single(); + if (nodes.IsCountOdd()) nodes = nodes.Append(new MerkleTreeEmptyNode(algorithm)).ToArray(); + + List mergedNodes = []; + + for (int index = 0; index < nodes.Count; index += 2) + { + MerkleTree left = nodes[index]; + MerkleTree right = nodes[index + 1]; + mergedNodes.Add(new MerkleTreeBranchNode(left, right, algorithm)); + } + + nodes = mergedNodes.ToArray(); + } + } +} diff --git a/OnixLabs.Security.Cryptography/MerkleTree.Equatable.cs b/OnixLabs.Security.Cryptography/MerkleTree.Equatable.cs index 0469861..8f94ce1 100644 --- a/OnixLabs.Security.Cryptography/MerkleTree.Equatable.cs +++ b/OnixLabs.Security.Cryptography/MerkleTree.Equatable.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,56 +16,58 @@ namespace OnixLabs.Security.Cryptography; -public abstract partial class MerkleTree : IEquatable +public abstract partial class MerkleTree { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(MerkleTree a, MerkleTree b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(MerkleTree? other) { - return Equals(a, b); + return ReferenceEquals(this, other) + || other is not null + && other.Hash == Hash; } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(MerkleTree a, MerkleTree b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return Equals(obj as MerkleTree); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public virtual bool Equals(MerkleTree? other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return ReferenceEquals(this, other) || other is not null && other.Hash == Hash; + return HashCode.Combine(GetType(), Hash); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(MerkleTree left, MerkleTree right) { - return Equals(obj as MerkleTree); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(MerkleTree left, MerkleTree right) { - return HashCode.Combine(GetType(), Hash); + return !Equals(left, right); } } diff --git a/OnixLabs.Security.Cryptography/MerkleTree.Generic.Create.cs b/OnixLabs.Security.Cryptography/MerkleTree.Generic.Create.cs new file mode 100644 index 0000000..d133d41 --- /dev/null +++ b/OnixLabs.Security.Cryptography/MerkleTree.Generic.Create.cs @@ -0,0 +1,62 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using OnixLabs.Core.Linq; + +namespace OnixLabs.Security.Cryptography; + +public abstract partial class MerkleTree +{ + /// + /// Creates a Merkle tree from the specified leaf nodes. + /// + /// The Merkle tree leaf nodes from which to build a Merkle tree. + /// The hash algorithm that will be used to hash together left-hand and right-hand nodes. + /// Returns a new node that represents the Merkle root. + public static MerkleTree Create(IEnumerable leaves, HashAlgorithm algorithm) + { + IReadOnlyList> nodes = leaves.Select(leaf => new MerkleTreeLeafNode(leaf, algorithm)).ToList(); + Require(nodes.IsNotEmpty(), "Cannot construct a Merkle tree from an empty list.", nameof(leaves)); + return BuildMerkleTree(nodes, algorithm); + } + + /// + /// Builds a Merkle tree from the specified nodes. + /// + /// The Merkle tree nodes from which to build a Merkle tree. + /// The hash algorithm that will be used to hash together left-hand and right-hand nodes. + /// Returns a new node that represents the Merkle root. + private static MerkleTree BuildMerkleTree(IReadOnlyList> nodes, HashAlgorithm algorithm) + { + while (true) + { + if (nodes.IsSingle()) return nodes.Single(); + if (nodes.IsCountOdd()) nodes = nodes.Append(new MerkleTreeEmptyNode(algorithm)).ToList(); + + List> mergedNodes = []; + + for (int index = 0; index < nodes.Count; index += 2) + { + MerkleTree left = nodes[index]; + MerkleTree right = nodes[index + 1]; + mergedNodes.Add(new MerkleTreeBranchNode(left, right, algorithm)); + } + + nodes = mergedNodes; + } + } +} diff --git a/OnixLabs.Security.Cryptography/MerkleTree.Generic.Equatable.cs b/OnixLabs.Security.Cryptography/MerkleTree.Generic.Equatable.cs new file mode 100644 index 0000000..0ff59da --- /dev/null +++ b/OnixLabs.Security.Cryptography/MerkleTree.Generic.Equatable.cs @@ -0,0 +1,73 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace OnixLabs.Security.Cryptography; + +public abstract partial class MerkleTree +{ + /// + /// Checks whether the current object is equal to another object of the same type. + /// + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(MerkleTree? other) + { + return ReferenceEquals(this, other) + || other is not null + && other.Hash == Hash; + } + + /// + /// Checks for equality between the current instance and another object. + /// + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) + { + return Equals(obj as MerkleTree); + } + + /// + /// Serves as a hash code function for the current instance. + /// + /// Returns a hash code for the current instance. + public override int GetHashCode() + { + return HashCode.Combine(GetType(), Hash); + } + + /// + /// Performs an equality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(MerkleTree left, MerkleTree right) + { + return Equals(left, right); + } + + /// + /// Performs an inequality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(MerkleTree left, MerkleTree right) + { + return !Equals(left, right); + } +} diff --git a/OnixLabs.Security.Cryptography/MerkleTree.Generic.Get.cs b/OnixLabs.Security.Cryptography/MerkleTree.Generic.Get.cs new file mode 100644 index 0000000..71c216c --- /dev/null +++ b/OnixLabs.Security.Cryptography/MerkleTree.Generic.Get.cs @@ -0,0 +1,81 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Security.Cryptography; + +namespace OnixLabs.Security.Cryptography; + +public abstract partial class MerkleTree +{ + /// + /// Obtains a hash-only, non-generic instance from the current instance. + /// + /// Returns a new hash-only, non-generic instance from the current instance. + public MerkleTree ToMerkleTree(HashAlgorithm algorithm) + { + IEnumerable hashes = GetLeafHashes(); + return Create(hashes, algorithm); + } + + /// + /// Obtains the leaf values from the current instance. + /// + /// Returns a new containing the leaf values from the current instance. + public IReadOnlyList GetLeafValues() + { + ICollection result = []; + CollectLeafValues(this, result); + return result.ToImmutableList(); + } + + /// + /// Recursively iterates through the specified to collect leaf hashes. + /// + /// The current from which to begin iterating through. + /// The collection that will contain the leaf hashes from the current . + protected override void CollectLeafHashes(MerkleTree current, ICollection hashes) + { + switch (current) + { + case MerkleTreeBranchNode branch: + CollectLeafHashes(branch.Left, hashes); + CollectLeafHashes(branch.Right, hashes); + break; + case MerkleTreeLeafNode leaf: + hashes.Add(leaf.Hash); + break; + } + } + + /// + /// Recursively iterates through the specified to collect leaf values. + /// + /// The current from which to begin iterating through. + /// The list that will contain the leaf values from the current . + private static void CollectLeafValues(MerkleTree current, ICollection values) + { + switch (current) + { + case MerkleTreeBranchNode branch: + CollectLeafValues(branch.Left, values); + CollectLeafValues(branch.Right, values); + break; + case MerkleTreeLeafNode leaf: + values.Add(leaf.Value); + break; + } + } +} diff --git a/OnixLabs.Security.Cryptography/MerkleTree.Generic.cs b/OnixLabs.Security.Cryptography/MerkleTree.Generic.cs new file mode 100644 index 0000000..3b9d788 --- /dev/null +++ b/OnixLabs.Security.Cryptography/MerkleTree.Generic.cs @@ -0,0 +1,95 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Security.Cryptography; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +/// +/// Represents a generic Merkle tree. +/// +public abstract partial class MerkleTree : MerkleTree, IValueEquatable> where T : IHashable +{ + /// + /// Initializes a new instance of the class. + /// This constructor is marked private to prevent external implementation. + /// + private MerkleTree(Hash hash) : base(hash) + { + } + + /// + /// Represents a generic Merkle tree branch node. + /// + private sealed class MerkleTreeBranchNode : MerkleTree + { + /// + /// Creates a new instance of the class. + /// + /// The left-hand node. + /// The right-hand node. + /// The hash algorithm that will be used to hash together the left-hand and right-hand nodes. + public MerkleTreeBranchNode(MerkleTree left, MerkleTree right, HashAlgorithm algorithm) : base(Hash.Concatenate(algorithm, left.Hash, right.Hash)) + { + Left = left; + Right = right; + } + + /// + /// Gets the left-hand node. + /// + public MerkleTree Left { get; } + + /// + /// Gets the right-hand node. + /// + public MerkleTree Right { get; } + } + + /// + /// Represents a generic Merkle tree leaf node. + /// + private sealed class MerkleTreeLeafNode : MerkleTree + { + /// + /// Creates a new instance of the class. + /// + /// The underlying value of the current node. + /// The hash algorithm that will be used to hash the specified value. + public MerkleTreeLeafNode(T value, HashAlgorithm algorithm) : base(value.ComputeHash(algorithm)) + { + Value = value; + } + + /// + /// Gets the underlying value of the current node. + /// + public T Value { get; } + } + + /// + /// Represents an empty generic Merkle tree node. + /// + private sealed class MerkleTreeEmptyNode : MerkleTree + { + /// + /// Creates a new instance of the class. + /// + /// The hash algorithm that will be used to determine the size of the required empty hash. + public MerkleTreeEmptyNode(HashAlgorithm algorithm) : base(new Hash(0x00, algorithm.HashSize / 8)) + { + } + } +} diff --git a/OnixLabs.Security.Cryptography/MerkleTree.Get.cs b/OnixLabs.Security.Cryptography/MerkleTree.Get.cs new file mode 100644 index 0000000..17a3e72 --- /dev/null +++ b/OnixLabs.Security.Cryptography/MerkleTree.Get.cs @@ -0,0 +1,52 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace OnixLabs.Security.Cryptography; + +public abstract partial class MerkleTree +{ + /// + /// Obtains the leaf hashes from the current instance. + /// + /// Returns a new instance containing the leaf hashes from the current instance. + public IReadOnlyList GetLeafHashes() + { + ICollection result = []; + CollectLeafHashes(this, result); + return result.ToImmutableList(); + } + + /// + /// Recursively iterates through the specified to collect leaf hashes. + /// + /// The current from which to begin iterating through. + /// The collection that will contain the leaf hashes from the current . + protected virtual void CollectLeafHashes(MerkleTree current, ICollection hashes) + { + switch (current) + { + case MerkleTreeBranchNode branch: + CollectLeafHashes(branch.Left, hashes); + CollectLeafHashes(branch.Right, hashes); + break; + case MerkleTreeLeafNode leaf: + hashes.Add(leaf.Hash); + break; + } + } +} diff --git a/OnixLabs.Security.Cryptography/Hmac.To.cs b/OnixLabs.Security.Cryptography/MerkleTree.To.cs similarity index 58% rename from OnixLabs.Security.Cryptography/Hmac.To.cs rename to OnixLabs.Security.Cryptography/MerkleTree.To.cs index f37933c..355bc24 100644 --- a/OnixLabs.Security.Cryptography/Hmac.To.cs +++ b/OnixLabs.Security.Cryptography/MerkleTree.To.cs @@ -1,38 +1,36 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; - namespace OnixLabs.Security.Cryptography; -public readonly partial struct Hmac +public abstract partial class MerkleTree { /// - /// Returns a that represents the current object. + /// Gets the underlying representation of the current instance. /// - /// A that represents the current object. - public override string ToString() + /// Return the underlying representation of the current instance. + public byte[] ToByteArray() { - return $"{Hash}:{Convert.ToHexString(Data).ToLower()}"; + return Hash.ToByteArray(); } /// - /// Returns a that represents the current object, including the hash algorithm type. + /// Returns a that represents the current object. /// - /// A that represents the current object, including the hash algorithm type. - public string ToStringWithAlgorithmType() + /// Returns that represents the current object. + public override string ToString() { - return $"{Hash.AlgorithmType.Name}:{ToString()}"; + return Hash.ToString(); } } diff --git a/OnixLabs.Security.Cryptography/MerkleTree.cs b/OnixLabs.Security.Cryptography/MerkleTree.cs index fca22af..cf6fab2 100644 --- a/OnixLabs.Security.Cryptography/MerkleTree.cs +++ b/OnixLabs.Security.Cryptography/MerkleTree.cs @@ -1,138 +1,93 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; -using System.Collections.Generic; -using System.Linq; -using OnixLabs.Core.Linq; -using static OnixLabs.Core.Preconditions; +using System.Security.Cryptography; namespace OnixLabs.Security.Cryptography; /// /// Represents a Merkle tree. /// -public abstract partial class MerkleTree +public abstract partial class MerkleTree : ICryptoPrimitive { /// - /// Prevents an instance of from being created. + /// Initializes a new instance of the class. + /// This constructor is marked internal to prevent external implementation. /// - internal MerkleTree() + internal MerkleTree(Hash hash) { + Hash = hash; } /// - /// Gets the hash of this node. + /// Gets the of the current node. /// - public abstract Hash Hash { get; } + public Hash Hash { get; } /// - /// Builds a Merkle tree from the specified leaf node hashes. + /// Represents a Merkle tree branch node. /// - /// The leaf nodes from which to build a Merkle tree. - /// Returns a new instance from the specified leaf nodes. - public static MerkleTree Build(IEnumerable nodes) + private sealed class MerkleTreeBranchNode : MerkleTree { - IReadOnlyList leafNodes = nodes.Select(MerkleTreeLeafNode.CreateHashNode).ToList(); - return Build(leafNodes); - } - - /// - /// Builds a Merkle tree from the specified Merkle tree nodes. - /// - /// The Merkle tree nodes from which to build a Merkle tree. - /// Returns a new instance from the specified nodes. - private static MerkleTree Build(IReadOnlyList nodes) - { - CheckIfMerkleTreesAreEmpty(nodes); - CheckNodesHaveEqualHashAlgorithms(nodes); - - return MergeMerkleTreeNodes(nodes); - } - - /// - /// Checks whether an is empty. - /// - /// The to check. - /// if the is empty. - private static void CheckIfMerkleTreesAreEmpty(IEnumerable merkleTrees) - { - Check(merkleTrees.IsNotEmpty(), "Cannot construct a merkle tree from an empty list."); - } - - /// - /// Checks whether all elements of an have the same hash algorithm type. - /// - /// The to check. - /// if the elements of the do not have the same hash algorithm type. - private static void CheckNodesHaveEqualHashAlgorithms(IEnumerable merkleTrees) - { - Check(merkleTrees.AllEqualBy(merkleTree => merkleTree.Hash.AlgorithmType), - "Cannot construct a merkle tree with different hash types."); - } + /// + /// Creates a new instance of the class. + /// + /// The left-hand node. + /// The right-hand node. + /// The hash algorithm that will be used to hash together the left-hand and right-hand nodes. + public MerkleTreeBranchNode(MerkleTree left, MerkleTree right, HashAlgorithm algorithm) : base(Hash.Concatenate(algorithm, left.Hash, right.Hash)) + { + Left = left; + Right = right; + } - /// - /// Ensures that an has an even number of elements. - /// If the contains an odd number of elements, then an all-zero hash - /// of the same will be inserted at the end of the list. - /// - /// The to ensure has an even number of elements. - /// Returns a new containing an even number of elements. - private static IReadOnlyList EnsureEvenNumberOfNodes(IReadOnlyList merkleTrees) - { - if (merkleTrees.IsCountEven()) return merkleTrees; + /// + /// Gets the left-hand node. + /// + public MerkleTree Left { get; } - HashAlgorithmType hashAlgorithmType = merkleTrees[0].Hash.AlgorithmType; - return merkleTrees.Append(MerkleTreeLeafNode.CreateEmptyNode(hashAlgorithmType)).ToList(); + /// + /// Gets the right-hand node. + /// + public MerkleTree Right { get; } } /// - /// Merges an of Merkle tree nodes. + /// Represents a Merkle tree leaf node. /// - /// The of Merkle tree nodes to merge. - /// Returns a merged of Merkle tree nodes. - private static MerkleTree MergeMerkleTreeNodes(IReadOnlyList merkleTrees) + private sealed class MerkleTreeLeafNode : MerkleTree { - while (true) + /// + /// Creates a new instance of the class. + /// + /// The value for the current node. + public MerkleTreeLeafNode(Hash hash) : base(hash) { - if (merkleTrees.IsSingle()) - { - return merkleTrees[0]; - } - - merkleTrees = EnsureEvenNumberOfNodes(merkleTrees); - - List mutableMerkleTrees = new(); - - for (int index = 0; index < merkleTrees.Count; index += 2) - { - MerkleTree leftMerkleTree = merkleTrees[index]; - MerkleTree rightMerkleTree = merkleTrees[index + 1]; - MerkleTree merkleTreeNode = new MerkleTreeBranchNode(leftMerkleTree, rightMerkleTree); - mutableMerkleTrees.Add(merkleTreeNode); - } - - merkleTrees = mutableMerkleTrees; } } /// - /// Returns a that represents the current object. + /// Represents an empty Merkle tree node. /// - /// A that represents the current object. - public override string ToString() + private sealed class MerkleTreeEmptyNode : MerkleTree { - return Hash.ToString(); + /// + /// Creates a new instance of the class. + /// + /// The hash algorithm that will be used to determine the size of the required empty hash. + public MerkleTreeEmptyNode(HashAlgorithm algorithm) : base(new Hash(0x00, algorithm.HashSize / 8)) + { + } } } diff --git a/OnixLabs.Security.Cryptography/MerkleTreeBranchNode.cs b/OnixLabs.Security.Cryptography/MerkleTreeBranchNode.cs deleted file mode 100644 index 40dc405..0000000 --- a/OnixLabs.Security.Cryptography/MerkleTreeBranchNode.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Security.Cryptography; - -/// -/// Represents a Merkle tree branch node. -/// -internal sealed class MerkleTreeBranchNode : MerkleTree -{ - /// - /// Creates a new instance of the class. - /// - /// The left-hand node. - /// The right-hand node. - public MerkleTreeBranchNode(MerkleTree left, MerkleTree right) - { - Left = left; - Right = right; - } - - /// - /// Gets the left-hand node. - /// - public MerkleTree Left { get; } - - /// - /// Gets the right-hand node. - /// - public MerkleTree Right { get; } - - /// - /// Gets the hash of this node. - /// - public override Hash Hash => Left.Hash.Concatenate(Right.Hash); -} diff --git a/OnixLabs.Security.Cryptography/MerkleTreeLeafNode.cs b/OnixLabs.Security.Cryptography/MerkleTreeLeafNode.cs deleted file mode 100644 index d73f785..0000000 --- a/OnixLabs.Security.Cryptography/MerkleTreeLeafNode.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Security.Cryptography; - -/// -/// Represents a Merkle tree leaf node. -/// -internal sealed class MerkleTreeLeafNode : MerkleTree -{ - /// - /// Creates a new instance of the class. - /// - public MerkleTreeLeafNode(Hash hash) - { - Hash = hash; - } - - /// - /// Gets the hash of this node. - /// - public override Hash Hash { get; } - - /// - /// Creates a from from the specified hash. - /// - /// The from which to create a node. - /// Returns an node. - public static MerkleTree CreateHashNode(Hash hash) - { - return new MerkleTreeLeafNode(hash); - } - - /// - /// Creates an empty node represented by an all-zero hash. - /// - /// The hash algorithm type of the node to create. - /// Returns an empty node represented by an all-zero hash. - public static MerkleTree CreateEmptyNode(HashAlgorithmType type) - { - return new MerkleTreeLeafNode(Hash.CreateAllZeroHash(type)); - } -} diff --git a/OnixLabs.Security.Cryptography/OnixLabs.Security.Cryptography.csproj b/OnixLabs.Security.Cryptography/OnixLabs.Security.Cryptography.csproj index 0e2dc8b..7dd2cf2 100644 --- a/OnixLabs.Security.Cryptography/OnixLabs.Security.Cryptography.csproj +++ b/OnixLabs.Security.Cryptography/OnixLabs.Security.Cryptography.csproj @@ -1,31 +1,48 @@ - - net6.0 + net8.0 OnixLabs.Security.Cryptography ONIXLabs ONIXLabs Cryptography API for .NET - 5.0.0 + 7.0.0 en enable true - Copyright © ONIXLabs 2020-2022 + Copyright © ONIXLabs 2020 https://github.com/onix-labs/onixlabs-dotnet - 5.0.0 - 10 + 7.0.0 + 12 - - - bin\Debug\net5.0\OnixLabs.Security.Cryptography.xml + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + embedded + true + true + true + Apache-2.0 + https://github.com/onix-labs/onixlabs-dotnet + README.md + true + true + git + https://github.com/onix-labs/onixlabs-dotnet - - - bin\Release\net5.0\OnixLabs.Security.Cryptography.xml - true + + true - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + - diff --git a/OnixLabs.Security.Cryptography/PrivateKey.Equatable.cs b/OnixLabs.Security.Cryptography/PrivateKey.Equatable.cs index 10cfd17..ae5332f 100644 --- a/OnixLabs.Security.Cryptography/PrivateKey.Equatable.cs +++ b/OnixLabs.Security.Cryptography/PrivateKey.Equatable.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,60 +18,59 @@ namespace OnixLabs.Security.Cryptography; -public abstract partial class PrivateKey : IEquatable +public abstract partial class PrivateKey { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(PrivateKey a, PrivateKey b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(PrivateKey? other) { - return Equals(a, b); + return ReferenceEquals(this, other) + || other is not null + && other.GetType() == GetType() + && other.KeyData.SequenceEqual(KeyData); } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(PrivateKey a, PrivateKey b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return Equals(obj as PrivateKey); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public virtual bool Equals(PrivateKey? other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return ReferenceEquals(this, other) - || other is not null - && other.GetType() == GetType() - && other.KeyData.SequenceEqual(KeyData) - && other.AlgorithmType == AlgorithmType; + return HashCode.Combine(GetType(), KeyData.GetContentHashCode()); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(PrivateKey left, PrivateKey right) { - return Equals(obj as PrivateKey); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(PrivateKey left, PrivateKey right) { - return HashCode.Combine(GetType(), AlgorithmType, KeyData.GetContentHashCode()); + return !Equals(left, right); } } diff --git a/OnixLabs.Security.Cryptography/PrivateKey.Export.cs b/OnixLabs.Security.Cryptography/PrivateKey.Export.cs index c09ebe6..8757e64 100644 --- a/OnixLabs.Security.Cryptography/PrivateKey.Export.cs +++ b/OnixLabs.Security.Cryptography/PrivateKey.Export.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,40 +20,16 @@ namespace OnixLabs.Security.Cryptography; public abstract partial class PrivateKey { /// - /// Exports the key in PKCS #8 format. + /// Exports the cryptographic private key data in PKCS #8 format. /// - /// Returns the key in PKCS #8 format. - public abstract byte[] ExportPkcs8Key(); + /// Returns a new instance containing the cryptographic private key data in PKCS #8 format. + public abstract byte[] ExportPkcs8PrivateKey(); /// - /// Exports the key in encrypted PKCS #8 format. + /// Exports the cryptographic private key data in encrypted PKCS #8 format. /// /// The password to use for encryption. /// The parameters required for password based encryption. - /// Returns the key in encrypted PKCS #8 format. - public abstract byte[] ExportPkcs8Key(ReadOnlySpan password, PbeParameters parameters); - - /// - /// Exports the key in encrypted PKCS #8 format. - /// - /// The password to use for encryption. - /// The parameters required for password based encryption. - /// Returns the key in encrypted PKCS #8 format. - public byte[] ExportPkcs8Key(char[] password, PbeParameters parameters) - { - ReadOnlySpan characters = password.AsSpan(); - return ExportPkcs8Key(characters, parameters); - } - - /// - /// Exports the key in encrypted PKCS #8 format. - /// - /// The password to use for encryption. - /// The parameters required for password based encryption. - /// Returns the key in encrypted PKCS #8 format. - public byte[] ExportPkcs8Key(string password, PbeParameters parameters) - { - char[] characters = password.ToCharArray(); - return ExportPkcs8Key(characters, parameters); - } + /// Returns a new instance containing the cryptographic private key data in PKCS #8 format. + public abstract byte[] ExportPkcs8PrivateKey(ReadOnlySpan password, PbeParameters parameters); } diff --git a/OnixLabs.Security.Cryptography/PrivateKey.Get.cs b/OnixLabs.Security.Cryptography/PrivateKey.Get.cs index 2c165e4..c264d2e 100644 --- a/OnixLabs.Security.Cryptography/PrivateKey.Get.cs +++ b/OnixLabs.Security.Cryptography/PrivateKey.Get.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,8 +17,8 @@ namespace OnixLabs.Security.Cryptography; public abstract partial class PrivateKey { /// - /// Gets the public key component from this private key. + /// Gets the cryptographic public key component from the current cryptographic private key. /// - /// Returns the public key component from this private key. + /// Returns a new instance containing the cryptographic public key component from the current cryptographic private key. public abstract PublicKey GetPublicKey(); } diff --git a/OnixLabs.Security.Cryptography/PrivateKey.Sign.cs b/OnixLabs.Security.Cryptography/PrivateKey.Sign.cs deleted file mode 100644 index 3caca47..0000000 --- a/OnixLabs.Security.Cryptography/PrivateKey.Sign.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Security.Cryptography; - -public abstract partial class PrivateKey -{ - /// - /// Computes a from the specified unsigned data. - /// - /// The unsigned data from which to compute a . - /// Returns a from the specified unsigned data. - public abstract DigitalSignature SignData(byte[] unsignedData); - - /// - /// Computes a from the specified unsigned hash. - /// - /// The unsigned hash from which to compute a . - /// Returns a from the specified unsigned hash. - public abstract DigitalSignature SignHash(byte[] unsignedHash); - - /// - /// Computes a from the specified unsigned hash. - /// - /// The unsigned hash from which to compute a . - /// Returns a from the specified unsigned hash. - public DigitalSignature SignHash(Hash unsignedHash) - { - byte[] unsignedHashBytes = unsignedHash.ToByteArray(); - return SignHash(unsignedHashBytes); - } -} diff --git a/OnixLabs.Security.Cryptography/PrivateKey.To.cs b/OnixLabs.Security.Cryptography/PrivateKey.To.cs index 8b72475..1f24ed1 100644 --- a/OnixLabs.Security.Cryptography/PrivateKey.To.cs +++ b/OnixLabs.Security.Cryptography/PrivateKey.To.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,81 +14,24 @@ using System; using OnixLabs.Core; -using OnixLabs.Core.Text; namespace OnixLabs.Security.Cryptography; public abstract partial class PrivateKey { /// - /// Returns a array that represents the underlying private key data. + /// Gets the underlying representation of the current instance. /// - /// Returns a array that represents the underlying private key data. + /// Return the underlying representation of the current instance. public byte[] ToByteArray() { return KeyData.Copy(); } /// - /// Returns a value that represents the underlying private key data. + /// Returns a that represents the current object. /// - /// Returns a value that represents the underlying private key data. - public Base16 ToBase16() - { - return Base16.FromByteArray(KeyData); - } - - /// - /// Returns a value that represents the underlying private key data. - /// - /// Returns a value that represents the underlying private key data. - public Base32 ToBase32() - { - return ToBase32(Base32Alphabet.Default); - } - - /// - /// Returns a value that represents the underlying private key data. - /// - /// The Base-32 alphabet to use to encode the private key data. - /// Returns a value that represents the underlying private key data. - public Base32 ToBase32(Base32Alphabet alphabet) - { - return Base32.FromByteArray(KeyData, alphabet); - } - - /// - /// Returns a value that represents the underlying private key data. - /// - /// Returns a value that represents the underlying private key data. - public Base58 ToBase58() - { - return ToBase58(Base58Alphabet.Default); - } - - /// - /// Returns a value that represents the underlying private key data. - /// - /// The Base-58 alphabet to use to encode the private key data. - /// Returns a value that represents the underlying private key data. - public Base58 ToBase58(Base58Alphabet alphabet) - { - return Base58.FromByteArray(KeyData, alphabet); - } - - /// - /// Returns a value that represents the underlying private key data. - /// - /// Returns a value that represents the underlying private key data. - public Base64 ToBase64() - { - return Base64.FromByteArray(KeyData); - } - - /// - /// Returns a that represents the current object. - /// - /// A that represents the current object. + /// Returns that represents the current object. public override string ToString() { return Convert.ToHexString(KeyData).ToLower(); diff --git a/OnixLabs.Security.Cryptography/PrivateKey.cs b/OnixLabs.Security.Cryptography/PrivateKey.cs index 5a74e98..4b9fa09 100644 --- a/OnixLabs.Security.Cryptography/PrivateKey.cs +++ b/OnixLabs.Security.Cryptography/PrivateKey.cs @@ -1,44 +1,29 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Security.Cryptography; /// -/// Represents the base class for private key implementations. +/// Represents a cryptographic private key. /// -public abstract partial class PrivateKey +/// The underlying key data of the cryptographic private key. +public abstract partial class PrivateKey(ReadOnlySpan keyData) : ICryptoPrimitive { /// - /// Initializes a new instance of the class. - /// - /// The private key data. - /// The hash algorithm type for computing signature data. - protected PrivateKey(byte[] data, HashAlgorithmType type) - { - type.GetHashAlgorithmName(); - - KeyData = data; - AlgorithmType = type; - } - - /// - /// Gets the underlying private key data. - /// - protected byte[] KeyData { get; } - - /// - /// Gets the hash algorithm type for computing signature data. + /// Gets the cryptographic private key data. /// - public HashAlgorithmType AlgorithmType { get; } + protected byte[] KeyData { get; } = keyData.ToArray(); } diff --git a/OnixLabs.Security.Cryptography/PublicKey.Equatable.cs b/OnixLabs.Security.Cryptography/PublicKey.Equatable.cs index 66ef8d0..5a6a110 100644 --- a/OnixLabs.Security.Cryptography/PublicKey.Equatable.cs +++ b/OnixLabs.Security.Cryptography/PublicKey.Equatable.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,60 +18,59 @@ namespace OnixLabs.Security.Cryptography; -public abstract partial class PublicKey : IEquatable +public abstract partial class PublicKey { /// - /// Performs an equality check between two object instances. + /// Checks whether the current object is equal to another object of the same type. /// - /// Instance a. - /// Instance b. - /// True if the instances are equal; otherwise, false. - public static bool operator ==(PublicKey a, PublicKey b) + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(PublicKey? other) { - return Equals(a, b); + return ReferenceEquals(this, other) + || other is not null + && other.GetType() == GetType() + && other.KeyData.SequenceEqual(KeyData); } /// - /// Performs an inequality check between two object instances. + /// Checks for equality between the current instance and another object. /// - /// Instance a. - /// Instance b. - /// True if the instances are not equal; otherwise, false. - public static bool operator !=(PublicKey a, PublicKey b) + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) { - return !Equals(a, b); + return Equals(obj as PublicKey); } /// - /// Checks for equality between this instance and another object. + /// Serves as a hash code function for the current instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public virtual bool Equals(PublicKey? other) + /// Returns a hash code for the current instance. + public override int GetHashCode() { - return ReferenceEquals(this, other) - || other is not null - && other.GetType() == GetType() - && other.KeyData.SequenceEqual(KeyData) - && other.AlgorithmType == AlgorithmType; + return HashCode.Combine(GetType(), KeyData.GetContentHashCode()); } /// - /// Checks for equality between this instance and another object. + /// Performs an equality comparison between two object instances. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(object? obj) + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(PublicKey left, PublicKey right) { - return Equals(obj as PublicKey); + return Equals(left, right); } /// - /// Serves as a hash code function for this instance. + /// Performs an inequality comparison between two object instances. /// - /// A hash code for this instance. - public override int GetHashCode() + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(PublicKey left, PublicKey right) { - return HashCode.Combine(GetType(), AlgorithmType, KeyData.GetContentHashCode()); + return !Equals(left, right); } } diff --git a/OnixLabs.Security.Cryptography/PublicKey.To.cs b/OnixLabs.Security.Cryptography/PublicKey.To.cs index 1292aa1..615fc5b 100644 --- a/OnixLabs.Security.Cryptography/PublicKey.To.cs +++ b/OnixLabs.Security.Cryptography/PublicKey.To.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,81 +14,24 @@ using System; using OnixLabs.Core; -using OnixLabs.Core.Text; namespace OnixLabs.Security.Cryptography; public abstract partial class PublicKey { /// - /// Returns a array that represents the underlying public key data. + /// Gets the underlying representation of the current instance. /// - /// Returns a array that represents the underlying public key data. + /// Return the underlying representation of the current instance. public byte[] ToByteArray() { return KeyData.Copy(); } /// - /// Returns a value that represents the underlying public key data. + /// Returns a that represents the current object. /// - /// Returns a value that represents the underlying public key data. - public Base16 ToBase16() - { - return Base16.FromByteArray(KeyData); - } - - /// - /// Returns a value that represents the underlying public key data. - /// - /// Returns a value that represents the underlying public key data. - public Base32 ToBase32() - { - return ToBase32(Base32Alphabet.Default); - } - - /// - /// Returns a value that represents the underlying public key data. - /// - /// The Base-32 alphabet to use to encode the public key data. - /// Returns a value that represents the underlying public key data. - public Base32 ToBase32(Base32Alphabet alphabet) - { - return Base32.FromByteArray(KeyData, alphabet); - } - - /// - /// Returns a value that represents the underlying public key data. - /// - /// Returns a value that represents the underlying public key data. - public Base58 ToBase58() - { - return ToBase58(Base58Alphabet.Default); - } - - /// - /// Returns a value that represents the underlying public key data. - /// - /// The Base-58 alphabet to use to encode the public key data. - /// Returns a value that represents the underlying public key data. - public Base58 ToBase58(Base58Alphabet alphabet) - { - return Base58.FromByteArray(KeyData, alphabet); - } - - /// - /// Returns a value that represents the underlying public key data. - /// - /// Returns a value that represents the underlying public key data. - public Base64 ToBase64() - { - return Base64.FromByteArray(KeyData); - } - - /// - /// Returns a that represents the current object. - /// - /// A that represents the current object. + /// Returns that represents the current object. public override string ToString() { return Convert.ToHexString(KeyData).ToLower(); diff --git a/OnixLabs.Security.Cryptography/PublicKey.Verify.cs b/OnixLabs.Security.Cryptography/PublicKey.Verify.cs deleted file mode 100644 index f8e6e2c..0000000 --- a/OnixLabs.Security.Cryptography/PublicKey.Verify.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Security.Cryptography; - -namespace OnixLabs.Security.Cryptography; - -public abstract partial class PublicKey -{ - /// - /// Determines whether the specified was signed by the private component of this public key. - /// - /// The to validate. - /// The unsigned data to validate. - /// Returns true if the specified was signed by the private component of this public key; otherwise, false. - public abstract bool IsDataValid(DigitalSignature signature, byte[] unsignedData); - - /// - /// Determines whether the specified was signed by the private component of this public key. - /// - /// The to validate. - /// The unsigned hash to validate. - /// Returns true if the specified was signed by the private component of this public key; otherwise, false. - public abstract bool IsHashValid(DigitalSignature signature, byte[] unsignedHash); - - /// - /// Determines whether the specified was signed by the private component of this public key. - /// - /// The to validate. - /// The unsigned hash to validate. - /// Returns true if the specified was signed by the private component of this public key; otherwise, false. - public bool IsHashValid(DigitalSignature signature, Hash unsignedHash) - { - byte[] unsignedHashBytes = unsignedHash.ToByteArray(); - return IsHashValid(signature, unsignedHashBytes); - } - - /// - /// Verifies whether the specified was signed by the private component of this public key. - /// - /// The to verify. - /// he unsigned data to verify. - /// If the specified was not signed by the private component of this public key. - public void VerifyData(DigitalSignature signature, byte[] unsignedData) - { - if (!IsDataValid(signature, unsignedData)) - { - throw new CryptographicException("The specified digital signature was not signed with this key."); - } - } - - /// - /// Verifies whether the specified was signed by the private component of this public key. - /// - /// The to verify. - /// he unsigned hash to verify. - /// If the specified was not signed by the private component of this public key. - public void VerifyHash(DigitalSignature signature, byte[] unsignedHash) - { - if (!IsHashValid(signature, unsignedHash)) - { - throw new CryptographicException("The specified digital signature was not signed with this key."); - } - } - - /// - /// Verifies whether the specified was signed by the private component of this public key. - /// - /// The to verify. - /// he unsigned hash to verify. - /// If the specified was not signed by the private component of this public key. - public void VerifyHash(DigitalSignature signature, Hash unsignedHash) - { - byte[] unsignedHashBytes = unsignedHash.ToByteArray(); - VerifyHash(signature, unsignedHashBytes); - } -} diff --git a/OnixLabs.Security.Cryptography/PublicKey.cs b/OnixLabs.Security.Cryptography/PublicKey.cs index a763b4d..a1bbd71 100644 --- a/OnixLabs.Security.Cryptography/PublicKey.cs +++ b/OnixLabs.Security.Cryptography/PublicKey.cs @@ -1,44 +1,29 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; + namespace OnixLabs.Security.Cryptography; /// -/// Represents the base class for public key implementations. +/// Represents a cryptographic public key. /// -public abstract partial class PublicKey +/// The underlying key data of the cryptographic public key. +public abstract partial class PublicKey(ReadOnlySpan keyData) : ICryptoPrimitive { /// - /// Creates a new instance of the class. - /// - /// The public key data. - /// The hash algorithm type for computing signature data. - protected PublicKey(byte[] data, HashAlgorithmType type) - { - type.GetHashAlgorithmName(); - - KeyData = data; - AlgorithmType = type; - } - - /// - /// Gets the underlying public key data. - /// - protected byte[] KeyData { get; } - - /// - /// Gets the hash algorithm type for computing signature data. + /// Gets the cryptographic public key data. /// - public HashAlgorithmType AlgorithmType { get; } + protected byte[] KeyData { get; } = keyData.ToArray(); } diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Create.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Create.cs new file mode 100644 index 0000000..ac6062c --- /dev/null +++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Create.cs @@ -0,0 +1,43 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Security.Cryptography; + +namespace OnixLabs.Security.Cryptography; + +public sealed partial class RsaPrivateKey +{ + /// + /// Creates a new RSA cryptographic private key. + /// + /// Returns a new instance. + public static RsaPrivateKey Create() + { + using RSA key = RSA.Create(); + byte[] keyData = key.ExportRSAPrivateKey(); + return new RsaPrivateKey(keyData); + } + + /// + /// Creates a new RSA cryptographic private key. + /// + /// The RSA parameters from which to create a new RSA cryptographic private key. + /// Returns a new instance. + public static RsaPrivateKey Create(RSAParameters parameters) + { + using RSA key = RSA.Create(parameters); + byte[] keyData = key.ExportRSAPrivateKey(); + return new RsaPrivateKey(keyData); + } +} diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Equatable.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Equatable.cs deleted file mode 100644 index d4c66fe..0000000 --- a/OnixLabs.Security.Cryptography/RsaPrivateKey.Equatable.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class RsaPrivateKey -{ - /// - /// Checks for equality between this instance and another object. - /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(PrivateKey? other) - { - return base.Equals(other) && other is RsaPrivateKey rsaOther && rsaOther.Padding == Padding; - } -} diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Export.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Export.cs index 3acf64c..2b16587 100644 --- a/OnixLabs.Security.Cryptography/RsaPrivateKey.Export.cs +++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Export.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,30 +20,24 @@ namespace OnixLabs.Security.Cryptography; public sealed partial class RsaPrivateKey { /// - /// Exports the key in PKCS #8 format. + /// Exports the RSA cryptographic private key data in PKCS #8 format. /// - /// Returns the key in PKCS #8 format. - public override byte[] ExportPkcs8Key() + /// Returns a new instance containing the RSA cryptographic private key data in PKCS #8 format. + public override byte[] ExportPkcs8PrivateKey() { - using RSA privateKey = RSA.Create(); - - privateKey.ImportRSAPrivateKey(KeyData, out int _); - - return privateKey.ExportPkcs8PrivateKey(); + using RSA key = ImportKeyData(); + return key.ExportPkcs8PrivateKey(); } /// - /// Exports the key in encrypted PKCS #8 format. + /// Exports the RSA cryptographic private key data in encrypted PKCS #8 format. /// /// The password to use for encryption. /// The parameters required for password based encryption. - /// Returns the key in encrypted PKCS #8 format. - public override byte[] ExportPkcs8Key(ReadOnlySpan password, PbeParameters parameters) + /// Returns a new instance containing the RSA cryptographic private key data in PKCS #8 format. + public override byte[] ExportPkcs8PrivateKey(ReadOnlySpan password, PbeParameters parameters) { - using RSA privateKey = RSA.Create(); - - privateKey.ImportRSAPrivateKey(KeyData, out int _); - - return privateKey.ExportEncryptedPkcs8PrivateKey(password, parameters); + using RSA key = ImportKeyData(); + return key.ExportEncryptedPkcs8PrivateKey(password, parameters); } } diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.From.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.From.cs deleted file mode 100644 index f3e3317..0000000 --- a/OnixLabs.Security.Cryptography/RsaPrivateKey.From.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Security.Cryptography; -using OnixLabs.Core.Text; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class RsaPrivateKey -{ - /// - /// Creates an from the specified key data and hash algorithm type. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPrivateKey FromByteArray(byte[] key, HashAlgorithmType type, RSASignaturePadding padding) - { - return new RsaPrivateKey(key, type, padding); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPrivateKey FromBase16(Base16 key, HashAlgorithmType type, RSASignaturePadding padding) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type, padding); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPrivateKey FromBase32(Base32 key, HashAlgorithmType type, RSASignaturePadding padding) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type, padding); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPrivateKey FromBase58(Base58 key, HashAlgorithmType type, RSASignaturePadding padding) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type, padding); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPrivateKey FromBase64(Base64 key, HashAlgorithmType type, RSASignaturePadding padding) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type, padding); - } -} diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Get.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Get.cs index a9e27cf..1c86fbf 100644 --- a/OnixLabs.Security.Cryptography/RsaPrivateKey.Get.cs +++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Get.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,16 +19,13 @@ namespace OnixLabs.Security.Cryptography; public sealed partial class RsaPrivateKey { /// - /// Gets the public key component from this private key. + /// Gets the RSA cryptographic public key component from the current RSA cryptographic private key. /// - /// Returns the public key component from this private key. - public override PublicKey GetPublicKey() + /// Returns a new instance containing the RSA cryptographic public key component from the current RSA cryptographic private key. + public override RsaPublicKey GetPublicKey() { - using RSA privateKey = RSA.Create(); - - privateKey.ImportRSAPrivateKey(KeyData, out int _); - byte[] publicKey = privateKey.ExportRSAPublicKey(); - - return new RsaPublicKey(publicKey, AlgorithmType, Padding); + using RSA key = ImportKeyData(); + byte[] keyData = key.ExportRSAPublicKey(); + return new RsaPublicKey(keyData); } } diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Import.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Import.cs index 7c1782b..5c07ad3 100644 --- a/OnixLabs.Security.Cryptography/RsaPrivateKey.Import.cs +++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Import.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,144 +20,63 @@ namespace OnixLabs.Security.Cryptography; public sealed partial class RsaPrivateKey { /// - /// Imports a PKCS #8 formatted key. + /// Imports the RSA cryptographic private key data in PKCS #8 format. /// - /// The key data to import. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static RsaPrivateKey ImportPkcs8Key(ReadOnlySpan data, HashAlgorithmType type, RSASignaturePadding padding) + /// The cryptographic private key data to import. + /// Returns a new instance from the imported cryptographic private key data. + public static RsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data) { - RSA privateKey = RSA.Create(); - - privateKey.ImportPkcs8PrivateKey(data, out int _); - byte[] bytes = privateKey.ExportRSAPrivateKey(); - - return FromByteArray(bytes, type, padding); - } - - /// - /// Imports a PKCS #8 formatted key. - /// - /// The key data to import. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static RsaPrivateKey ImportPkcs8Key(byte[] data, HashAlgorithmType type, RSASignaturePadding padding) - { - ReadOnlySpan bytes = data.AsSpan(); - return ImportPkcs8Key(bytes, type, padding); - } - - /// - /// Imports an encrypted PKCS #8 formatted key. - /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static RsaPrivateKey ImportPkcs8Key( - ReadOnlySpan data, - ReadOnlySpan password, - HashAlgorithmType type, - RSASignaturePadding padding) - { - RSA privateKey = RSA.Create(); - - privateKey.ImportEncryptedPkcs8PrivateKey(password, data, out int _); - byte[] bytes = privateKey.ExportRSAPrivateKey(); - - return FromByteArray(bytes, type, padding); - } - - /// - /// Imports an encrypted PKCS #8 formatted key. - /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static RsaPrivateKey ImportPkcs8Key( - ReadOnlySpan data, - char[] password, - HashAlgorithmType type, - RSASignaturePadding padding) - { - ReadOnlySpan characters = password.AsSpan(); - return ImportPkcs8Key(data, characters, type, padding); + return ImportPkcs8PrivateKey(data, out int _); } /// - /// Imports an encrypted PKCS #8 formatted key. + /// Imports the RSA cryptographic private key data in PKCS #8 format. /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static RsaPrivateKey ImportPkcs8Key( - ReadOnlySpan data, - string password, - HashAlgorithmType type, - RSASignaturePadding padding) + /// The cryptographic private key data to import. + /// The number of bytes read from the input data. + /// Returns a new instance from the imported cryptographic private key data. + public static RsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, out int bytesRead) { - ReadOnlySpan characters = password.AsSpan(); - return ImportPkcs8Key(data, characters, type, padding); + using RSA key = RSA.Create(); + key.ImportPkcs8PrivateKey(data, out bytesRead); + byte[] keyData = key.ExportRSAPrivateKey(); + return new RsaPrivateKey(keyData); } /// - /// Imports an encrypted PKCS #8 formatted key. + /// Imports the RSA cryptographic private key data in encrypted PKCS #8 format. /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static RsaPrivateKey ImportPkcs8Key( - byte[] data, - ReadOnlySpan password, - HashAlgorithmType type, - RSASignaturePadding padding) + /// The cryptographic private key data to import. + /// The password required for password based decryption. + /// Returns a new instance from the imported cryptographic private key data. + public static RsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, ReadOnlySpan password) { - ReadOnlySpan bytes = data.AsSpan(); - return ImportPkcs8Key(bytes, password, type, padding); + return ImportPkcs8PrivateKey(data, password, out int _); } /// - /// Imports an encrypted PKCS #8 formatted key. + /// Imports the RSA cryptographic private key data in encrypted PKCS #8 format. /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static RsaPrivateKey ImportPkcs8Key( - byte[] data, - char[] password, - HashAlgorithmType type, - RSASignaturePadding padding) + /// The cryptographic private key data to import. + /// The password required for password based decryption. + /// The number of bytes read from the input data. + /// Returns a new instance from the imported cryptographic private key data. + public static RsaPrivateKey ImportPkcs8PrivateKey(ReadOnlySpan data, ReadOnlySpan password, out int bytesRead) { - ReadOnlySpan bytes = data.AsSpan(); - return ImportPkcs8Key(bytes, password, type, padding); + using RSA key = RSA.Create(); + key.ImportEncryptedPkcs8PrivateKey(password, data, out bytesRead); + byte[] keyData = key.ExportRSAPrivateKey(); + return new RsaPrivateKey(keyData); } /// - /// Imports an encrypted PKCS #8 formatted key. + /// Imports the key data into a new instance. /// - /// The key data to import. - /// The password to decrypt the key data. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified PKCS #8 key data. - public static RsaPrivateKey ImportPkcs8Key( - byte[] data, - string password, - HashAlgorithmType type, - RSASignaturePadding padding) + /// Returns a new instance containing the imported key data. + private RSA ImportKeyData() { - ReadOnlySpan bytes = data.AsSpan(); - return ImportPkcs8Key(bytes, password, type, padding); + RSA algorithm = RSA.Create(); + algorithm.ImportRSAPrivateKey(KeyData, out int _); + return algorithm; } } diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Sign.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Sign.cs index 5463d6d..f1908fb 100644 --- a/OnixLabs.Security.Cryptography/RsaPrivateKey.Sign.cs +++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Sign.cs @@ -1,52 +1,103 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.IO; using System.Security.Cryptography; +using OnixLabs.Core; namespace OnixLabs.Security.Cryptography; public sealed partial class RsaPrivateKey { /// - /// Computes a from the specified unsigned data. + /// Hashes the specified data and signs the resulting hash. /// - /// The unsigned data from which to compute a . - /// Returns a from the specified unsigned data. - public override DigitalSignature SignData(byte[] unsignedData) + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding) { - using RSA privateKey = RSA.Create(); + using RSA key = ImportKeyData(); + return key.SignData(data, algorithm, padding); + } - privateKey.ImportRSAPrivateKey(KeyData, out int _); - HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName(); - byte[] signedData = privateKey.SignData(unsignedData, name, Padding); + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that will be used to hash the input data. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.SignData(data.Slice(offset, count), algorithm, padding); + } - return DigitalSignature.FromByteArray(signedData); + /// + /// Hashes the specified data and signs the resulting hash. + /// + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.SignData(data, algorithm, padding); } /// - /// Computes a from the specified unsigned hash. + /// Hashes the specified data and signs the resulting hash. /// - /// The unsigned hash from which to compute a . - /// Returns a from the specified unsigned hash. - public override DigitalSignature SignHash(byte[] unsignedHash) + /// The input data to hash and sign. + /// The hash algorithm that will be used to hash the input data. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignData(IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding) { - using RSA privateKey = RSA.Create(); + using RSA key = ImportKeyData(); + return key.SignData(data.ToByteArray(), algorithm, padding); + } - privateKey.ImportRSAPrivateKey(KeyData, out int _); - HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName(); - byte[] signedData = privateKey.SignHash(unsignedHash, name, Padding); + /// + /// Signs the specified . + /// + /// The hash to sign. + /// The hash algorithm that will be used to hash the input hash. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignHash(Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.SignHash(hash.ToByteArray(), algorithm, padding); + } - return DigitalSignature.FromByteArray(signedData); + /// + /// Signs the specified . + /// + /// The hash to sign. + /// The hash algorithm that will be used to hash the input hash. + /// The RSA signature padding mode that will be used to generate the cryptographic digital signature. + /// Returns a new instance containing the cryptographic digital signature. + public byte[] SignHash(ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.SignHash(hash, algorithm, padding); } } diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.cs index 5d6d3e4..c5c462f 100644 --- a/OnixLabs.Security.Cryptography/RsaPrivateKey.cs +++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.cs @@ -1,39 +1,23 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Security.Cryptography; +using System; namespace OnixLabs.Security.Cryptography; /// -/// Represents an RSA private key. +/// Represents an RSA cryptographic private key. /// -public sealed partial class RsaPrivateKey : PrivateKey -{ - /// - /// Creates a new instance of the class. - /// - /// The private key data. - /// The hash algorithm type for computing signature data. - /// The for computing signature data. - internal RsaPrivateKey(byte[] data, HashAlgorithmType type, RSASignaturePadding padding) : base(data, type) - { - Padding = padding; - } - - /// - /// Gets the for computing signature data. - /// - public RSASignaturePadding Padding { get; } -} +/// The underlying key data of the RSA cryptographic private key. +public sealed partial class RsaPrivateKey(ReadOnlySpan keyData) : PrivateKey(keyData), IRsaPrivateKey; diff --git a/OnixLabs.Security.Cryptography/RsaPublicKey.From.cs b/OnixLabs.Security.Cryptography/RsaPublicKey.From.cs deleted file mode 100644 index b024572..0000000 --- a/OnixLabs.Security.Cryptography/RsaPublicKey.From.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020-2022 ONIXLabs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Security.Cryptography; -using OnixLabs.Core.Text; - -namespace OnixLabs.Security.Cryptography; - -public sealed partial class RsaPublicKey -{ - /// - /// Creates an from the specified key data and hash algorithm type. - /// - /// The key data from which to construct a public key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPublicKey FromByteArray(byte[] key, HashAlgorithmType type, RSASignaturePadding padding) - { - return new RsaPublicKey(key, type, padding); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPublicKey FromBase16(Base16 key, HashAlgorithmType type, RSASignaturePadding padding) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type, padding); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPublicKey FromBase32(Base32 key, HashAlgorithmType type, RSASignaturePadding padding) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type, padding); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPublicKey FromBase58(Base58 key, HashAlgorithmType type, RSASignaturePadding padding) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type, padding); - } - - /// - /// Creates an from the specified value. - /// - /// The key data from which to construct a private key. - /// The for computing signature data. - /// The for computing signature data. - /// Returns an from the specified key data and hash algorithm type. - public static RsaPublicKey FromBase64(Base64 key, HashAlgorithmType type, RSASignaturePadding padding) - { - byte[] bytes = key.ToByteArray(); - return FromByteArray(bytes, type, padding); - } -} diff --git a/OnixLabs.Security.Cryptography/RsaPublicKey.Equatable.cs b/OnixLabs.Security.Cryptography/RsaPublicKey.Import.cs similarity index 61% rename from OnixLabs.Security.Cryptography/RsaPublicKey.Equatable.cs rename to OnixLabs.Security.Cryptography/RsaPublicKey.Import.cs index 21fd875..827a138 100644 --- a/OnixLabs.Security.Cryptography/RsaPublicKey.Equatable.cs +++ b/OnixLabs.Security.Cryptography/RsaPublicKey.Import.cs @@ -1,28 +1,31 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System.Security.Cryptography; + namespace OnixLabs.Security.Cryptography; public sealed partial class RsaPublicKey { /// - /// Checks for equality between this instance and another object. + /// Imports the key data into a new instance. /// - /// The object to check for equality. - /// true if the object is equal to this instance; otherwise, false. - public override bool Equals(PublicKey? other) + /// Returns a new instance containing the imported key data. + private RSA ImportKeyData() { - return base.Equals(other) && other is RsaPublicKey rsaOther && rsaOther.Padding == Padding; + RSA algorithm = RSA.Create(); + algorithm.ImportRSAPublicKey(KeyData, out int _); + return algorithm; } } diff --git a/OnixLabs.Security.Cryptography/RsaPublicKey.Verify.cs b/OnixLabs.Security.Cryptography/RsaPublicKey.Verify.cs index c71ea71..c383d76 100644 --- a/OnixLabs.Security.Cryptography/RsaPublicKey.Verify.cs +++ b/OnixLabs.Security.Cryptography/RsaPublicKey.Verify.cs @@ -1,54 +1,430 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +using System; +using System.IO; using System.Security.Cryptography; +using OnixLabs.Core; namespace OnixLabs.Security.Cryptography; public sealed partial class RsaPublicKey { /// - /// Determines whether the specified was signed by the private component of this public key. + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. /// - /// The to validate. - /// The unsigned data to validate. - /// Returns true if the specified was signed by the private component of this public key; otherwise, false. - public override bool IsDataValid(DigitalSignature signature, byte[] unsignedData) + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding) { - using RSA publicKey = RSA.Create(); + using RSA key = ImportKeyData(); + return key.VerifyData(data, signature, algorithm, padding); + } + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyData(data.Slice(offset, count), signature, algorithm, padding); + } - publicKey.ImportRSAPublicKey(KeyData, out int _); - byte[] signatureData = signature.ToByteArray(); - HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName(); + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyData(data, signature.ToArray(), algorithm, padding); + } + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyData(data.ToByteArray(), signature, algorithm, padding); + } - return publicKey.VerifyData(unsignedData, signatureData, name, Padding); + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyData(data, signature.ToByteArray(), algorithm, padding); + } + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyData(data.Slice(offset, count), signature.ToByteArray(), algorithm, padding); + } + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyData(data, signature.ToByteArray(), algorithm, padding); + } + + /// + /// Determines whether the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified data was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsDataValid(DigitalSignature signature, IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyData(data.ToByteArray(), signature.ToByteArray(), algorithm, padding); + } + + /// + /// Determines whether the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsHashValid(ReadOnlySpan signature, ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyHash(hash, signature, algorithm, padding); + } + + /// + /// Determines whether the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsHashValid(DigitalSignature signature, ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyHash(hash, signature.ToByteArray(), algorithm, padding); + } + + /// + /// Determines whether the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsHashValid(ReadOnlySpan signature, Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyHash(hash.ToByteArray(), signature, algorithm, padding); + } + + /// + /// Determines whether the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// + /// Returns if the specified hash was signed by the RSA cryptographic private key that corresponds to the + /// current RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature; otherwise, . + /// + public bool IsHashValid(DigitalSignature signature, Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + using RSA key = ImportKeyData(); + return key.VerifyHash(hash.ToByteArray(), signature.ToByteArray(), algorithm, padding); + } + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsDataValid(signature, data, algorithm, padding)); + } + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyData(ReadOnlySpan signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsDataValid(signature, data, offset, count, algorithm, padding)); + } + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyData(ReadOnlySpan signature, Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsDataValid(signature, data, algorithm, padding)); + } + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyData(ReadOnlySpan signature, IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsDataValid(signature, data, algorithm, padding)); + } + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyData(DigitalSignature signature, ReadOnlySpan data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsDataValid(signature, data, algorithm, padding)); + } + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyData(DigitalSignature signature, ReadOnlySpan data, int offset, int count, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsDataValid(signature, data, offset, count, algorithm, padding)); + } + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyData(DigitalSignature signature, Stream data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsDataValid(signature, data, algorithm, padding)); + } + + /// + /// Verifies that the specified data was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed data matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned data to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input data. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyData(DigitalSignature signature, IBinaryConvertible data, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsDataValid(signature, data, algorithm, padding)); + } + + /// + /// Verifies that the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyHash(ReadOnlySpan signature, ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsHashValid(signature, hash, algorithm, padding)); + } + + /// + /// Verifies that the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyHash(DigitalSignature signature, ReadOnlySpan hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsHashValid(signature, hash, algorithm, padding)); + } + + /// + /// Verifies that the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyHash(ReadOnlySpan signature, Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsHashValid(signature, hash, algorithm, padding)); + } + + /// + /// Verifies that the specified hash was signed by the RSA cryptographic private key that corresponds to the current + /// RSA cryptographic public key, and that the signed hash matches the specified cryptographic digital signature. + /// + /// The signature to validate against the current RSA cryptographic public key. + /// The unsigned hash to validate against the current RSA cryptographic public key. + /// The hash algorithm that was used to hash and sign the input hash. + /// The RSA signature padding mode that was used to generate the cryptographic digital signature. + /// If the specified signature could not be verified. + public void VerifyHash(DigitalSignature signature, Hash hash, HashAlgorithmName algorithm, RSASignaturePadding padding) + { + CheckSignature(IsHashValid(signature, hash, algorithm, padding)); } /// - /// Determines whether the specified was signed by the private component of this public key. + /// Performs a signature check pre-condition that throws a in the event that the specified condition is . /// - /// The to validate. - /// The unsigned hash to validate. - /// Returns true if the specified was signed by the private component of this public key; otherwise, false. - public override bool IsHashValid(DigitalSignature signature, byte[] unsignedHash) + /// The signature condition to check. + /// If the specified condition is . + private static void CheckSignature(bool condition) { - using RSA publicKey = RSA.Create(); + if (condition) return; - publicKey.ImportRSAPublicKey(KeyData, out int _); - byte[] signatureData = signature.ToByteArray(); - HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName(); + const string message = "The specified digital signature could not be verified. Either the specified data is incorrect, " + + "or the data was not signed with a private key corresponding to the current public key."; - return publicKey.VerifyHash(unsignedHash, signatureData, name, Padding); + throw new CryptographicException(message); } } diff --git a/OnixLabs.Security.Cryptography/RsaPublicKey.cs b/OnixLabs.Security.Cryptography/RsaPublicKey.cs index 1bcba28..418dfc3 100644 --- a/OnixLabs.Security.Cryptography/RsaPublicKey.cs +++ b/OnixLabs.Security.Cryptography/RsaPublicKey.cs @@ -1,39 +1,23 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Security.Cryptography; +using System; namespace OnixLabs.Security.Cryptography; /// -/// Represents an RSA public key. +/// Represents an RSA cryptographic public key. /// -public sealed partial class RsaPublicKey : PublicKey -{ - /// - /// Creates a new instance of the class. - /// - /// The public key data. - /// The hash algorithm type for computing signature data. - /// The for computing signature data. - internal RsaPublicKey(byte[] data, HashAlgorithmType type, RSASignaturePadding padding) : base(data, type) - { - Padding = padding; - } - - /// - /// Gets the for computing signature data. - /// - public RSASignaturePadding Padding { get; } -} +/// The underlying key data of the RSA cryptographic public key. +public sealed partial class RsaPublicKey(ReadOnlySpan keyData) : PublicKey(keyData), IRsaPublicKey; diff --git a/OnixLabs.Security.Cryptography/Salt.Create.cs b/OnixLabs.Security.Cryptography/Salt.Create.cs new file mode 100644 index 0000000..f694002 --- /dev/null +++ b/OnixLabs.Security.Cryptography/Salt.Create.cs @@ -0,0 +1,46 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Security.Cryptography; + +namespace OnixLabs.Security.Cryptography; + +public readonly partial struct Salt +{ + /// + /// Creates a new instance of the specified length. + /// + /// The length of the salt to create. + /// Returns a new instance of the specified length. + public static Salt Create(int length) + { + using RandomNumberGenerator generator = RandomNumberGenerator.Create(); + byte[] value = new byte[length]; + generator.GetBytes(value); + return new Salt(value); + } + + /// + /// Creates a new, non-zero instance of the specified length. + /// + /// The length of the salt to create. + /// Returns a new non-zero instance of the specified length. + public static Salt CreateNonZero(int length) + { + using RandomNumberGenerator generator = RandomNumberGenerator.Create(); + byte[] value = new byte[length]; + generator.GetNonZeroBytes(value); + return new Salt(value); + } +} diff --git a/OnixLabs.Security.Cryptography/Salt.Equatable.cs b/OnixLabs.Security.Cryptography/Salt.Equatable.cs new file mode 100644 index 0000000..c8d713c --- /dev/null +++ b/OnixLabs.Security.Cryptography/Salt.Equatable.cs @@ -0,0 +1,72 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Linq; +using OnixLabs.Core.Linq; + +namespace OnixLabs.Security.Cryptography; + +public readonly partial struct Salt +{ + /// + /// Checks whether the current object is equal to another object of the same type. + /// + /// An object to compare with the current object. + /// Returns if the current object is equal to the other parameter; otherwise, . + public bool Equals(Salt other) + { + return value.SequenceEqual(other.value); + } + + /// + /// Checks for equality between the current instance and another object. + /// + /// The object to check for equality. + /// Returns if the object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) + { + return obj is Salt other && Equals(other); + } + + /// + /// Serves as a hash code function for the current instance. + /// + /// Returns a hash code for the current instance. + public override int GetHashCode() + { + return value.GetContentHashCode(); + } + + /// + /// Performs an equality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is equal to the right-hand instance; otherwise, . + public static bool operator ==(Salt left, Salt right) + { + return Equals(left, right); + } + + /// + /// Performs an inequality comparison between two object instances. + /// + /// The left-hand instance to compare. + /// The right-hand instance to compare. + /// Returns if the left-hand instance is not equal to the right-hand instance; otherwise, . + public static bool operator !=(Salt left, Salt right) + { + return !Equals(left, right); + } +} diff --git a/OnixLabs.Security.Cryptography/Salt.To.cs b/OnixLabs.Security.Cryptography/Salt.To.cs new file mode 100644 index 0000000..100dd40 --- /dev/null +++ b/OnixLabs.Security.Cryptography/Salt.To.cs @@ -0,0 +1,39 @@ +// Copyright 2020 ONIXLabs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using OnixLabs.Core; + +namespace OnixLabs.Security.Cryptography; + +public readonly partial struct Salt +{ + /// + /// Gets the underlying representation of the current instance. + /// + /// Return the underlying representation of the current instance. + public byte[] ToByteArray() + { + return value.Copy(); + } + + /// + /// Returns a that represents the current object. + /// + /// Returns that represents the current object. + public override string ToString() + { + return Convert.ToHexString(value).ToLower(); + } +} diff --git a/OnixLabs.Security.Cryptography/Hash.Empty.cs b/OnixLabs.Security.Cryptography/Salt.cs similarity index 60% rename from OnixLabs.Security.Cryptography/Hash.Empty.cs rename to OnixLabs.Security.Cryptography/Salt.cs index b5bb1a1..b773cde 100644 --- a/OnixLabs.Security.Cryptography/Hash.Empty.cs +++ b/OnixLabs.Security.Cryptography/Salt.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,18 +16,11 @@ namespace OnixLabs.Security.Cryptography; -public readonly partial struct Hash +/// +/// Represents a cryptographically secure random number, otherwise known as a salt value. +/// +/// The underlying value of the salt. +public readonly partial struct Salt(ReadOnlySpan value) : ICryptoPrimitive { - /// - /// Gets an empty hash value. - /// - public static readonly Hash Empty; - - /// - /// Initializes static members of the class. - /// - static Hash() - { - Empty = FromByteArray(Array.Empty(), HashAlgorithmType.Unknown); - } + private readonly byte[] value = value.ToArray(); } diff --git a/OnixLabs.Security.Cryptography/Sha3.Create.cs b/OnixLabs.Security.Cryptography/Sha3.Create.cs index d10375c..a6cfaaf 100644 --- a/OnixLabs.Security.Cryptography/Sha3.Create.cs +++ b/OnixLabs.Security.Cryptography/Sha3.Create.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,7 +20,7 @@ public abstract partial class Sha3 /// Creates an instance of the algorithm. /// /// An instance of the algorithm. - public static Sha3 CreateSha3Hash224() + public static Sha3Hash224 CreateSha3Hash224() { return new Sha3Hash224(); } @@ -29,7 +29,7 @@ public static Sha3 CreateSha3Hash224() /// Creates an instance of the algorithm. /// /// An instance of the algorithm. - public static Sha3 CreateSha3Hash256() + public static Sha3Hash256 CreateSha3Hash256() { return new Sha3Hash256(); } @@ -38,7 +38,7 @@ public static Sha3 CreateSha3Hash256() /// Creates an instance of the algorithm. /// /// An instance of the algorithm. - public static Sha3 CreateSha3Hash384() + public static Sha3Hash384 CreateSha3Hash384() { return new Sha3Hash384(); } @@ -47,7 +47,7 @@ public static Sha3 CreateSha3Hash384() /// Creates an instance of the algorithm. /// /// An instance of the algorithm. - public static Sha3 CreateSha3Hash512() + public static Sha3Hash512 CreateSha3Hash512() { return new Sha3Hash512(); } @@ -57,7 +57,7 @@ public static Sha3 CreateSha3Hash512() /// /// The output length of the hash in bytes. /// An instance of the algorithm. - public static Sha3 CreateSha3Shake128(int length) + public static Sha3Shake128 CreateSha3Shake128(int length) { return new Sha3Shake128(length); } @@ -67,7 +67,7 @@ public static Sha3 CreateSha3Shake128(int length) /// /// The output length of the hash in bytes. /// An instance of the algorithm. - public static Sha3 CreateSha3Shake256(int length) + public static Sha3Shake256 CreateSha3Shake256(int length) { return new Sha3Shake256(length); } diff --git a/OnixLabs.Security.Cryptography/Sha3.Permute.cs b/OnixLabs.Security.Cryptography/Sha3.Permute.cs index 948cf03..81bdc1c 100644 --- a/OnixLabs.Security.Cryptography/Sha3.Permute.cs +++ b/OnixLabs.Security.Cryptography/Sha3.Permute.cs @@ -1,18 +1,18 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System.Runtime.CompilerServices; +using System.Collections.Generic; namespace OnixLabs.Security.Cryptography; @@ -22,22 +22,21 @@ public abstract partial class Sha3 /// Performs the FIPS 202 SHA-3 permutation. /// /// The state upon which to perform the permutation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Permute(ulong[] state) + private static void Permute(IList state) { const int hashRounds = 24; ulong c0, c1, c2, c3, c4, d0, d1, d2, d3, d4; ulong[] roundConstants = - { + [ 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A, 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 - }; + ]; for (int round = 0; round < hashRounds; round++) { @@ -47,7 +46,8 @@ private static void Permute(ulong[] state) Iota(round); } - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + return; + void Theta() { c0 = state[0] ^ state[5] ^ state[10] ^ state[15] ^ state[20]; @@ -89,7 +89,6 @@ void Theta() state[24] ^= d4; } - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] void RhoPi() { ulong final = RotateLeft(state[1], 1); @@ -120,16 +119,15 @@ void RhoPi() state[10] = final; } - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] void Chi() { for (int i = 0; i < 25; i += 5) { - c0 = state[0 + i] ^ ((~state[1 + i]) & state[2 + i]); - c1 = state[1 + i] ^ ((~state[2 + i]) & state[3 + i]); - c2 = state[2 + i] ^ ((~state[3 + i]) & state[4 + i]); - c3 = state[3 + i] ^ ((~state[4 + i]) & state[0 + i]); - c4 = state[4 + i] ^ ((~state[0 + i]) & state[1 + i]); + c0 = state[0 + i] ^ (~state[1 + i] & state[2 + i]); + c1 = state[1 + i] ^ (~state[2 + i] & state[3 + i]); + c2 = state[2 + i] ^ (~state[3 + i] & state[4 + i]); + c3 = state[3 + i] ^ (~state[4 + i] & state[0 + i]); + c4 = state[4 + i] ^ (~state[0 + i] & state[1 + i]); state[0 + i] = c0; state[1 + i] = c1; @@ -139,13 +137,11 @@ void Chi() } } - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] void Iota(int round) { state[0] ^= roundConstants[round]; } - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] ulong RotateLeft(ulong x, byte y) { return (x << y) | (x >> (64 - y)); diff --git a/OnixLabs.Security.Cryptography/Sha3.cs b/OnixLabs.Security.Cryptography/Sha3.cs index 34f8a7f..16be5f7 100644 --- a/OnixLabs.Security.Cryptography/Sha3.cs +++ b/OnixLabs.Security.Cryptography/Sha3.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -42,10 +42,10 @@ public abstract partial class Sha3 : HashAlgorithm /// private readonly int delimiter; - /// - /// The length of the hash in bits. - /// - private readonly int bitLength; + // /// + // /// The length of the hash in bits. + // /// + // private readonly int bitLength; /// /// The state block size. @@ -65,15 +65,15 @@ public abstract partial class Sha3 : HashAlgorithm /// /// The permutable sponge state. /// - private ulong[] state = Array.Empty(); + private ulong[] state = []; /// /// The hash result. /// - private byte[] result = Array.Empty(); + private byte[] result = []; /// - /// Creates a new instance of the class. + /// Initializes a new instance of the class. /// /// The rate in bytes of the sponge state. /// The state delimiter. @@ -82,9 +82,15 @@ protected Sha3(int rateBytes, int delimiter, int bitLength) { this.rateBytes = rateBytes; this.delimiter = delimiter; - this.bitLength = bitLength; + + HashSize = bitLength; } + /// + /// Gets the size, in bits, of the computed hash code. + /// + public override int HashSize { get; } + /// /// Initializes an implementation of the class. /// @@ -94,7 +100,7 @@ public override void Initialize() inputPointer = default; outputPointer = default; state = new ulong[25]; - result = new byte[bitLength / 8]; + result = new byte[HashSize / 8]; } /// @@ -113,8 +119,8 @@ protected override void HashCore(byte[] array, int ibStart, int cbSize) for (int index = ibStart; index < blockSize; index++) { - byte x = Convert.ToByte(Buffer.GetByte(state, index) ^ array[index + inputPointer]); - Buffer.SetByte(state, index, x); + byte value = Convert.ToByte(Buffer.GetByte(state, index) ^ array[index + inputPointer]); + Buffer.SetByte(state, index, value); } inputPointer += blockSize; @@ -135,7 +141,7 @@ protected override byte[] HashFinal() byte pad = Convert.ToByte(Buffer.GetByte(state, blockSize) ^ delimiter); Buffer.SetByte(state, blockSize, pad); - if (((delimiter & 0x80) != 0) && blockSize == (rateBytes - 1)) + if ((delimiter & 0x80) != 0 && blockSize == rateBytes - 1) { Permute(state); } @@ -144,7 +150,7 @@ protected override byte[] HashFinal() Buffer.SetByte(state, rateBytes - 1, pad); Permute(state); - int outputBytesLeft = bitLength / 8; + int outputBytesLeft = HashSize / 8; while (outputBytesLeft > 0) { diff --git a/OnixLabs.Security.Cryptography/Sha3Hash224.cs b/OnixLabs.Security.Cryptography/Sha3Hash224.cs index a2a375e..6000511 100644 --- a/OnixLabs.Security.Cryptography/Sha3Hash224.cs +++ b/OnixLabs.Security.Cryptography/Sha3Hash224.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,7 +30,7 @@ public sealed class Sha3Hash224 : Sha3 private const int BitLength = 224; /// - /// Creates a new instance of the class. + /// Initializes a new instance of the class. /// public Sha3Hash224() : base(RateBytes, HashDelimiter, BitLength) { diff --git a/OnixLabs.Security.Cryptography/Sha3Hash256.cs b/OnixLabs.Security.Cryptography/Sha3Hash256.cs index c81f188..a2aa2aa 100644 --- a/OnixLabs.Security.Cryptography/Sha3Hash256.cs +++ b/OnixLabs.Security.Cryptography/Sha3Hash256.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,7 +30,7 @@ public sealed class Sha3Hash256 : Sha3 private const int BitLength = 256; /// - /// Creates a new instance of the class. + /// Initializes a new instance of the class. /// public Sha3Hash256() : base(RateBytes, HashDelimiter, BitLength) { diff --git a/OnixLabs.Security.Cryptography/Sha3Hash384.cs b/OnixLabs.Security.Cryptography/Sha3Hash384.cs index 373d70d..a38d290 100644 --- a/OnixLabs.Security.Cryptography/Sha3Hash384.cs +++ b/OnixLabs.Security.Cryptography/Sha3Hash384.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,7 +30,7 @@ public sealed class Sha3Hash384 : Sha3 private const int BitLength = 384; /// - /// Creates a new instance of the class. + /// Initializes a new instance of the class. /// public Sha3Hash384() : base(RateBytes, HashDelimiter, BitLength) { diff --git a/OnixLabs.Security.Cryptography/Sha3Hash512.cs b/OnixLabs.Security.Cryptography/Sha3Hash512.cs index 528e9a1..51b8c1c 100644 --- a/OnixLabs.Security.Cryptography/Sha3Hash512.cs +++ b/OnixLabs.Security.Cryptography/Sha3Hash512.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,7 +30,7 @@ public sealed class Sha3Hash512 : Sha3 private const int BitLength = 512; /// - /// Creates a new instance of the class. + /// Initializes a new instance of the class. /// public Sha3Hash512() : base(RateBytes, HashDelimiter, BitLength) { diff --git a/OnixLabs.Security.Cryptography/Sha3Shake128.cs b/OnixLabs.Security.Cryptography/Sha3Shake128.cs index ca3e692..4b112c4 100644 --- a/OnixLabs.Security.Cryptography/Sha3Shake128.cs +++ b/OnixLabs.Security.Cryptography/Sha3Shake128.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,7 +30,7 @@ public sealed class Sha3Shake128 : Sha3 private const int BitLengthMultiplier = 8; /// - /// Creates a new instance of the class. + /// Initializes a new instance of the class. /// /// The hash output length in bytes. public Sha3Shake128(int length) : base(RateBytes, ShakeDelimiter, length * BitLengthMultiplier) diff --git a/OnixLabs.Security.Cryptography/Sha3Shake256.cs b/OnixLabs.Security.Cryptography/Sha3Shake256.cs index fb82f5c..e413541 100644 --- a/OnixLabs.Security.Cryptography/Sha3Shake256.cs +++ b/OnixLabs.Security.Cryptography/Sha3Shake256.cs @@ -1,11 +1,11 @@ -// Copyright 2020-2022 ONIXLabs -// +// Copyright © 2020 ONIXLabs +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,7 +30,7 @@ public sealed class Sha3Shake256 : Sha3 private const int BitLengthMultiplier = 8; /// - /// Creates a new instance of the class. + /// Initializes a new instance of the class. /// /// The hash output length in bytes. public Sha3Shake256(int length) : base(RateBytes, ShakeDelimiter, length * BitLengthMultiplier) diff --git a/README.md b/README.md index ec13de8..ca44623 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,34 @@ ![ONIX Labs](https://raw.githubusercontent.com/onix-labs/onixlabs-website/main/src/assets/images/logo/full/original/original-md.png) -# ONIXLabs Core APIs for .NET +# ONIXLabs .NET Library -This repository contains the ONIXLabs Core APIs for .NET +Welcome to the ONIXLabs .NET Library, a comprehensive and meticulously crafted suite of APIs engineered to empower developers and enrich the developer experience. With the ONIXLabs .NET Library, developers gain access to a wealth of tools and resources aimed at streamlining development workflows, enhancing code quality, and fortifying application security. Whether you're a seasoned .NET developer or embarking on your coding journey, ONIXLabs is your trusted companion for building robust and secure software solutions. + +## ONIXLabs Core + +At the heart of the ONIXLabs .NET Library lies the Core module, serving as the bedrock for building resilient and scalable applications. Here, developers will find essential APIs and interfaces meticulously designed to promote code reusability, maintainability, and readability. From foundational interfaces to extension methods enriching object manipulation, ONIXLabs Core sets the stage for seamless development experiences. + +- **Core APIs**: Includes interfaces such as `IValueEquatable`, `IValueComparable`, and `IBinaryConvertible`, providing a foundation for implementing common patterns and functionalities. +- **Extension Methods**: Extends the capabilities of objects, arrays, strings, and enumerables, making data manipulation more intuitive and efficient. +- **Strong Enumeration Pattern**: Offers an implementation of the strong enumeration pattern, for strongly typed enumerations which can be extended with value and associated behaviour. +- **Text Services**: Provides codecs for various encoding schemes like Base16, Base32, Base58, and Base64, along with primitive structs representing these bases for semantic clarity. + +## ONIXLabs Numerics + +Numerical computations are a fundamental aspect of many software applications, often requiring precision, flexibility, and performance. The ONIXLabs Numerics module equips developers with a suite of tools tailored to meet these demands. Whether it's obtaining detailed insights into numeric values or performing complex arithmetic operations, this module empowers developers to tackle numerical challenges with ease and confidence. + +- **NumberInfo**: A versatile struct that provides detailed insights into any numeric value, including unscaled value, scale, significand, exponent, precision, and sign. +- **BigDecimal**: An arbitrary-length decimal number representation leveraging .NET's generic math interfaces. It supports a comprehensive range of arithmetic operations, parsing, conversion, and culture-specific formatting, facilitating precise numerical calculations. +- **Generic Math Extension Methods**: Extends the functionality of numerical types with useful generic math extension methods, enhancing flexibility and productivity in numerical computing tasks. + +## ONIXLabs Cryptography + +In an era marked by increasing cybersecurity threats, robust cryptographic implementations are paramount. The ONIXLabs Cryptography module offers developers a comprehensive set of APIs for handling cryptographic operations securely. From digital signatures and hash functions to Merkle trees and FIPS-202 compliant SHA-3 implementations, developers can leverage ONIXLabs Cryptography to safeguard sensitive data and ensure the integrity and confidentiality of their applications. + +- **DigitalSignature**: Wraps digital signatures, providing a convenient abstraction for cryptographic signature operations. +- **Hash**: Wraps hash functions, facilitating secure hashing of data with ease. +- **MerkleTree**: Represents Merkle trees, enabling efficient verification of data integrity in distributed systems. +- **Salt**: Represents cryptographically secure random numbers, enhancing security in cryptographic operations. +- **Public and Private Key Abstractions**: Offers abstractions for public and private keys, including implementations for RSA and ECDSA, along with functions for signing and verification. +- **SHA-3 Implementation**: A complete, platform-independent, FIPS-202 implementation of SHA-3, including variants like SHA-3 224, 256, 384, 512, SHAKE128, and SHAKE256, ensuring compliance with cryptographic standards. diff --git a/onixlabs-dotnet.sln b/onixlabs-dotnet.sln index c2af2ab..4bfcd86 100644 --- a/onixlabs-dotnet.sln +++ b/onixlabs-dotnet.sln @@ -1,11 +1,5 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Framework", "Framework", "{6E4FE7B0-5E44-4EB4-B64F-228645C4F79B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Playground", "Playground", "{039F52FD-BF3C-4E6A-B0AB-1F34E48ADBFF}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{C1D7FFDA-A234-4A01-9371-19C68DD74C91}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Core", "OnixLabs.Core\OnixLabs.Core.csproj", "{1EDC1164-0205-433D-A356-00DAD4686264}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Playground", "OnixLabs.Playground\OnixLabs.Playground.csproj", "{23F9EB42-D9D4-4C43-9C99-3D2A1F8866B7}" @@ -16,17 +10,37 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Security.Cryptogra EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Security.Cryptography.UnitTests", "OnixLabs.Security.Cryptography.UnitTests\OnixLabs.Security.Cryptography.UnitTests.csproj", "{C3DE665B-5B02-41DE-8BB0-C53A1326E162}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Core.UnitTests.Data", "OnixLabs.Core.UnitTests.Data\OnixLabs.Core.UnitTests.Data.csproj", "{E3FBC79F-7F5A-4ECF-9653-C0AB203FF5D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Security.Cryptography.UnitTests.Data", "OnixLabs.Security.Cryptography.UnitTests.Data\OnixLabs.Security.Cryptography.UnitTests.Data.csproj", "{513CFC09-42E2-43C3-BB68-90640D78CDF4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Numerics", "OnixLabs.Numerics\OnixLabs.Numerics.csproj", "{571D23F3-BC70-4646-A63C-844AED8291E3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Numerics.UnitTests", "OnixLabs.Numerics.UnitTests\OnixLabs.Numerics.UnitTests.csproj", "{5D14F804-600B-4CFF-8900-53F66D7821E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Numerics.UnitTests.Data", "OnixLabs.Numerics.UnitTests.Data\OnixLabs.Numerics.UnitTests.Data.csproj", "{3A85C8CF-D5EF-4130-8CC0-8AAB07983707}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{45B3DB8D-3344-4CB5-A6B9-A587514056EA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Numerics", "Numerics", "{95655D21-93CD-4A49-AAB2-E7A262E7B730}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Security.Cryptography", "Security.Cryptography", "{564C7FAB-BEA0-4B10-BE14-6C151E0ACE6E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution - {1EDC1164-0205-433D-A356-00DAD4686264} = {6E4FE7B0-5E44-4EB4-B64F-228645C4F79B} - {23F9EB42-D9D4-4C43-9C99-3D2A1F8866B7} = {039F52FD-BF3C-4E6A-B0AB-1F34E48ADBFF} - {893547DB-4EAA-4D1F-886F-0D6114F8601E} = {C1D7FFDA-A234-4A01-9371-19C68DD74C91} - {78FDB1BA-6545-41DE-99B8-F007C6A6B093} = {6E4FE7B0-5E44-4EB4-B64F-228645C4F79B} - {C3DE665B-5B02-41DE-8BB0-C53A1326E162} = {C1D7FFDA-A234-4A01-9371-19C68DD74C91} + {1EDC1164-0205-433D-A356-00DAD4686264} = {45B3DB8D-3344-4CB5-A6B9-A587514056EA} + {E3FBC79F-7F5A-4ECF-9653-C0AB203FF5D5} = {45B3DB8D-3344-4CB5-A6B9-A587514056EA} + {893547DB-4EAA-4D1F-886F-0D6114F8601E} = {45B3DB8D-3344-4CB5-A6B9-A587514056EA} + {571D23F3-BC70-4646-A63C-844AED8291E3} = {95655D21-93CD-4A49-AAB2-E7A262E7B730} + {5D14F804-600B-4CFF-8900-53F66D7821E8} = {95655D21-93CD-4A49-AAB2-E7A262E7B730} + {3A85C8CF-D5EF-4130-8CC0-8AAB07983707} = {95655D21-93CD-4A49-AAB2-E7A262E7B730} + {78FDB1BA-6545-41DE-99B8-F007C6A6B093} = {564C7FAB-BEA0-4B10-BE14-6C151E0ACE6E} + {C3DE665B-5B02-41DE-8BB0-C53A1326E162} = {564C7FAB-BEA0-4B10-BE14-6C151E0ACE6E} + {513CFC09-42E2-43C3-BB68-90640D78CDF4} = {564C7FAB-BEA0-4B10-BE14-6C151E0ACE6E} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1EDC1164-0205-433D-A356-00DAD4686264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -49,5 +63,25 @@ Global {C3DE665B-5B02-41DE-8BB0-C53A1326E162}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3DE665B-5B02-41DE-8BB0-C53A1326E162}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3DE665B-5B02-41DE-8BB0-C53A1326E162}.Release|Any CPU.Build.0 = Release|Any CPU + {E3FBC79F-7F5A-4ECF-9653-C0AB203FF5D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3FBC79F-7F5A-4ECF-9653-C0AB203FF5D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3FBC79F-7F5A-4ECF-9653-C0AB203FF5D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3FBC79F-7F5A-4ECF-9653-C0AB203FF5D5}.Release|Any CPU.Build.0 = Release|Any CPU + {513CFC09-42E2-43C3-BB68-90640D78CDF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {513CFC09-42E2-43C3-BB68-90640D78CDF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {513CFC09-42E2-43C3-BB68-90640D78CDF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {513CFC09-42E2-43C3-BB68-90640D78CDF4}.Release|Any CPU.Build.0 = Release|Any CPU + {571D23F3-BC70-4646-A63C-844AED8291E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {571D23F3-BC70-4646-A63C-844AED8291E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {571D23F3-BC70-4646-A63C-844AED8291E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {571D23F3-BC70-4646-A63C-844AED8291E3}.Release|Any CPU.Build.0 = Release|Any CPU + {5D14F804-600B-4CFF-8900-53F66D7821E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D14F804-600B-4CFF-8900-53F66D7821E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D14F804-600B-4CFF-8900-53F66D7821E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D14F804-600B-4CFF-8900-53F66D7821E8}.Release|Any CPU.Build.0 = Release|Any CPU + {3A85C8CF-D5EF-4130-8CC0-8AAB07983707}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A85C8CF-D5EF-4130-8CC0-8AAB07983707}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A85C8CF-D5EF-4130-8CC0-8AAB07983707}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A85C8CF-D5EF-4130-8CC0-8AAB07983707}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/onixlabs-dotnet.sln.DotSettings b/onixlabs-dotnet.sln.DotSettings new file mode 100644 index 0000000..be92f32 --- /dev/null +++ b/onixlabs-dotnet.sln.DotSettings @@ -0,0 +1,75 @@ + + Copyright 2020-${CurrentDate.Year} ONIXLabs + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True \ No newline at end of file