diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 9975a7d..a46d563 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -29,8 +29,8 @@ jobs:
- name: Build
run: dotnet build --no-restore
- name: Test (.NET 8.0)
- run: dotnet test --no-build --framework net8.0 --verbosity quiet
+ run: dotnet test --no-build --output Normal --no-progress --framework net8.0
- name: Test (.NET 9.0)
- run: dotnet test --no-build --framework net9.0 --verbosity quiet
+ run: dotnet test --no-build --output Normal --no-progress --framework net9.0
- name: Test (.NET 10.0)
- run: dotnet test --no-build --framework net10.0 --verbosity quiet
+ run: dotnet test --no-build --output Normal --no-progress --framework net10.0
diff --git a/OnixLabs.Numerics/GenericMath.cs b/OnixLabs.Numerics/GenericMath.cs
index 25971b4..0c52162 100644
--- a/OnixLabs.Numerics/GenericMath.cs
+++ b/OnixLabs.Numerics/GenericMath.cs
@@ -40,7 +40,7 @@ public static class GenericMath
/// Returns the factorial of the specified value.
public static BigInteger Factorial(T value) where T : IBinaryInteger
{
- Require(value >= T.Zero, "Value must be greater than or equal to zero.");
+ Require(value >= T.Zero, "Value must be greater than or equal to zero.", nameof(value));
if (value <= T.One) return BigInteger.One;
@@ -84,7 +84,7 @@ public static T Pow10(int exponent) where T : INumber
{
Require(exponent >= 0, "Exponent must be greater than, or equal to zero.", nameof(exponent));
- if (exponent == 0)
+ if (exponent is 0)
return T.One;
T result = T.One;
@@ -92,7 +92,7 @@ public static T Pow10(int exponent) where T : INumber
while (exponent > 0)
{
- if ((exponent & 1) == 1)
+ if ((exponent & 1) is 1)
result *= baseValue;
baseValue *= baseValue;
diff --git a/OnixLabs.Playground/OnixLabs.Playground.csproj b/OnixLabs.Playground/OnixLabs.Playground.csproj
index 3c25aa1..bbd8a80 100644
--- a/OnixLabs.Playground/OnixLabs.Playground.csproj
+++ b/OnixLabs.Playground/OnixLabs.Playground.csproj
@@ -8,6 +8,7 @@
+
diff --git a/OnixLabs.Units.UnitTests/AreaTests.cs b/OnixLabs.Units.UnitTests/AreaTests.cs
new file mode 100644
index 0000000..6686bc7
--- /dev/null
+++ b/OnixLabs.Units.UnitTests/AreaTests.cs
@@ -0,0 +1,558 @@
+// Copyright 2020-2025 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR 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;
+
+namespace OnixLabs.Units.UnitTests;
+
+public sealed class AreaTests
+{
+ // IEEE-754 binary floating-point arithmetic causes small discrepancies in calculation, therefore we need a tolerance.
+ private const double Tolerance = 1e+85;
+
+ [Fact(DisplayName = "Area.Empty should produce the expected result")]
+ public void AreaEmptyShouldProduceExpectedResult()
+ {
+ // Given / When
+ Area area = Area.Zero;
+
+ // Then
+ Assert.Equal(0.0, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareYoctoMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1.0)]
+ [InlineData(2.5, 2.5)]
+ public void AreaFromSquareYoctoMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareYoctoMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareZeptoMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e6)]
+ [InlineData(2.5, 2.5e6)]
+ public void AreaFromSquareZeptoMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareZeptoMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareAttoMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e12)]
+ [InlineData(2.5, 2.5e12)]
+ public void AreaFromSquareAttoMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareAttoMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareFemtoMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e18)]
+ [InlineData(2.5, 2.5e18)]
+ public void AreaFromSquareFemtoMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareFemtoMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquarePicoMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e24)]
+ [InlineData(2.5, 2.5e24)]
+ public void AreaFromSquarePicoMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquarePicoMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareNanoMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e30)]
+ [InlineData(2.5, 2.5e30)]
+ public void AreaFromSquareNanoMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareNanoMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareMicroMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e36)]
+ [InlineData(2.5, 2.5e36)]
+ public void AreaFromSquareMicroMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareMicroMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareMilliMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e42)]
+ [InlineData(2.5, 2.5e42)]
+ public void AreaFromSquareMilliMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareMilliMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareCentiMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e44)]
+ [InlineData(2.5, 2.5e44)]
+ public void AreaFromSquareCentiMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareCentiMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareDeciMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e46)]
+ [InlineData(2.5, 2.5e46)]
+ public void AreaFromSquareDeciMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareDeciMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e48)]
+ [InlineData(2.5, 2.5e48)]
+ public void AreaFromSquareMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareDecaMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e50)]
+ [InlineData(2.5, 2.5e50)]
+ public void AreaFromSquareDecaMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareDecaMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareHectoMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e52)]
+ [InlineData(2.5, 2.5e52)]
+ public void AreaFromSquareHectoMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareHectoMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareKiloMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e54)]
+ [InlineData(2.5, 2.5e54)]
+ public void AreaFromSquareKiloMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareKiloMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareMegaMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e60)]
+ [InlineData(2.5, 2.5e60)]
+ public void AreaFromSquareMegaMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareMegaMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareGigaMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e66)]
+ [InlineData(2.5, 2.5e66)]
+ public void AreaFromSquareGigaMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareGigaMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareTeraMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e72)]
+ [InlineData(2.5, 2.5e72)]
+ public void AreaFromSquareTeraMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareTeraMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquarePetaMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e78)]
+ [InlineData(2.5, 2.5e78)]
+ public void AreaFromSquarePetaMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquarePetaMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareExaMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e84)]
+ [InlineData(2.5, 2.5e84)]
+ public void AreaFromSquareExaMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareExaMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareZettaMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e90)]
+ [InlineData(2.5, 2.5e90)]
+ public void AreaFromSquareZettaMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareZettaMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareYottaMeters should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e96)]
+ [InlineData(2.5, 2.5e96)]
+ public void AreaFromSquareYottaMetersShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareYottaMeters(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareInches should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 6.4516e44)]
+ [InlineData(2.5, 1.6129e45)]
+ public void AreaFromSquareInchesShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareInches(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareFeet should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 9.290304e46)]
+ [InlineData(2.5, 2.322576e47)]
+ public void AreaFromSquareFeetShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareFeet(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareYards should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 8.3612736e47)]
+ [InlineData(2.5, 2.0903184e48)]
+ public void AreaFromSquareYardsShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareYards(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromSquareMiles should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 2.589988110336e54)]
+ [InlineData(2.5, 6.47497027584e54)]
+ public void AreaFromSquareMilesShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromSquareMiles(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromAcres should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 4.0468564224e51)]
+ [InlineData(2.5, 1.0117141056e52)]
+ public void AreaFromAcresShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromAcres(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Area.FromHectares should produce the expected SquareYoctoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e52)]
+ [InlineData(2.5, 2.5e52)]
+ public void AreaFromHectaresShouldProduceExpectedSquareYoctoMeters(double value, double expectedSquareYoctoMeters)
+ {
+ // Given / When
+ Area area = Area.FromHectares(value);
+
+ // Then
+ Assert.Equal(expectedSquareYoctoMeters, area.SquareYoctoMeters, Tolerance);
+ }
+
+ [Fact(DisplayName = "Area.Add should produce the expected result")]
+ public void AreaAddShouldProduceExpectedValue()
+ {
+ // Given
+ Area left = Area.FromSquareMeters(1500.0);
+ Area right = Area.FromSquareMeters(500.0);
+
+ // When
+ Area result = left.Add(right);
+
+ // Then
+ Assert.Equal(2000.0, result.SquareMeters, Tolerance);
+ }
+
+ [Fact(DisplayName = "Area.Subtract should produce the expected result")]
+ public void AreaSubtractShouldProduceExpectedValue()
+ {
+ // Given
+ Area left = Area.FromSquareMeters(1500.0);
+ Area right = Area.FromSquareMeters(400.0);
+
+ // When
+ Area result = left.Subtract(right);
+
+ // Then
+ Assert.Equal(1100.0, result.SquareMeters, Tolerance);
+ }
+
+ [Fact(DisplayName = "Area.Multiply should produce the expected result")]
+ public void AreaMultiplyShouldProduceExpectedValue()
+ {
+ // Given
+ Area left = Area.FromSquareMeters(10.0); // 1e49 sqym
+ Area right = Area.FromSquareMeters(3.0); // 3e48 sqym
+
+ // When
+ Area result = left.Multiply(right); // 1e49 * 3e48 = 3e97 sqym
+
+ // Then
+ Assert.Equal(1e49, left.SquareYoctoMeters, Tolerance);
+ Assert.Equal(3e48, right.SquareYoctoMeters, Tolerance);
+ Assert.Equal(3e97, result.SquareYoctoMeters, Tolerance);
+ }
+
+ [Fact(DisplayName = "Area.Divide should produce the expected result")]
+ public void AreaDivideShouldProduceExpectedValue()
+ {
+ // Given
+ Area left = Area.FromSquareMeters(100.0); // 1e50 sqym
+ Area right = Area.FromSquareMeters(20.0); // 2e49 sqym
+
+ // When
+ Area result = left.Divide(right); // 1e50 / 2e49 = 5 sqym
+
+ // Then
+ Assert.Equal(5.0, result.SquareYoctoMeters, Tolerance);
+ Assert.Equal(5e-48, result.SquareMeters, Tolerance);
+ }
+
+ [Fact(DisplayName = "Area comparison should produce the expected result (left equal to right)")]
+ public void AreaComparisonShouldProduceExpectedResultLeftEqualToRight()
+ {
+ // Given
+ Area left = Area.FromSquareMeters(1234.0);
+ Area right = Area.FromSquareMeters(1234.0);
+
+ // When / Then
+ Assert.Equal(0, Area.Compare(left, right));
+ Assert.Equal(0, left.CompareTo(right));
+ Assert.Equal(0, left.CompareTo((object)right));
+ Assert.False(left > right);
+ Assert.True(left >= right);
+ Assert.False(left < right);
+ Assert.True(left <= right);
+ }
+
+ [Fact(DisplayName = "Area comparison should produce the expected result (left greater than right)")]
+ public void AreaComparisonShouldProduceExpectedLeftGreaterThanRight()
+ {
+ // Given
+ Area left = Area.FromSquareMeters(4567.0);
+ Area right = Area.FromSquareMeters(1234.0);
+
+ // When / Then
+ Assert.Equal(1, Area.Compare(left, right));
+ Assert.Equal(1, left.CompareTo(right));
+ Assert.Equal(1, left.CompareTo((object)right));
+ Assert.True(left > right);
+ Assert.True(left >= right);
+ Assert.False(left < right);
+ Assert.False(left <= right);
+ }
+
+ [Fact(DisplayName = "Area comparison should produce the expected result (left less than right)")]
+ public void AreaComparisonShouldProduceExpectedLeftLessThanRight()
+ {
+ // Given
+ Area left = Area.FromSquareMeters(1234.0);
+ Area right = Area.FromSquareMeters(4567.0);
+
+ // When / Then
+ Assert.Equal(-1, Area.Compare(left, right));
+ Assert.Equal(-1, left.CompareTo(right));
+ Assert.Equal(-1, left.CompareTo((object)right));
+ Assert.False(left > right);
+ Assert.False(left >= right);
+ Assert.True(left < right);
+ Assert.True(left <= right);
+ }
+
+ [Fact(DisplayName = "Area equality should produce the expected result (left equal to right)")]
+ public void AreaEqualityShouldProduceExpectedResultLeftEqualToRight()
+ {
+ // Given
+ Area left = Area.FromSquareKiloMeters(2.0); // 2 km²
+ Area right = Area.FromSquareMeters(2_000_000.0); // 2,000,000 m²
+
+ // When / Then
+ Assert.True(Area.Equals(left, right));
+ Assert.True(left.Equals(right));
+ Assert.True(left.Equals((object)right));
+ Assert.True(left == right);
+ Assert.False(left != right);
+ }
+
+ [Fact(DisplayName = "Area equality should produce the expected result (left not equal to right)")]
+ public void AreaEqualityShouldProduceExpectedResultLeftNotEqualToRight()
+ {
+ // Given
+ Area left = Area.FromSquareMeters(2.0);
+ Area right = Area.FromSquareMeters(2.5);
+
+ // When / Then
+ Assert.False(Area.Equals(left, right));
+ Assert.False(left.Equals(right));
+ Assert.False(left.Equals((object)right));
+ Assert.False(left == right);
+ Assert.True(left != right);
+ }
+
+ [Fact(DisplayName = "Area.ToString should produce the expected result")]
+ public void AreaToStringShouldProduceExpectedResult()
+ {
+ // Given
+ Area area = Area.FromSquareMeters(1_000_000.0); // 1 km²
+
+ // When / Then
+ Assert.Equal("1,000,000.000 sqm", $"{area:sqm3}");
+ Assert.Equal("1.000 sqkm", $"{area:sqkm3}");
+ Assert.Equal("10,000,000,000.000 sqcm", $"{area:sqcm3}");
+ Assert.Equal("1,550,003,100.006 sqin", $"{area:sqin3}");
+ Assert.Equal("10,763,910.417 sqft", $"{area:sqft3}");
+ Assert.Equal("1,195,990.046 sqyd", $"{area:sqyd3}");
+ Assert.Equal("0.386 sqmi", $"{area:sqmi3}");
+ Assert.Equal("100.000 ha", $"{area:ha3}");
+ Assert.Equal("247.105 ac", $"{area:ac3}");
+ }
+
+ [Fact(DisplayName = "Area.ToString should honor custom culture separators")]
+ public void AreaToStringShouldHonorCustomCulture()
+ {
+ // Given
+ CultureInfo customCulture = new("de-DE");
+ Area area = Area.FromSquareMeters(1234.56);
+
+ // When
+ string formatted = area.ToString("sqm2", customCulture);
+
+ // Then
+ // German uses '.' for thousands and ',' for decimals.
+ Assert.Equal("1.234,56 sqm", formatted);
+ }
+}
diff --git a/OnixLabs.Units.UnitTests/DataSizeTests.cs b/OnixLabs.Units.UnitTests/DataSizeTests.cs
new file mode 100644
index 0000000..b13f6ce
--- /dev/null
+++ b/OnixLabs.Units.UnitTests/DataSizeTests.cs
@@ -0,0 +1,613 @@
+// Copyright 2020-2025 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR 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.Units.UnitTests;
+
+public sealed class DataSizeTests
+{
+ // IEEE-754 binary floating-point arithmetic causes small discrepancies in calculation, therefore we need a tolerance.
+ private const double Tolerance = 1e-12;
+
+ [Fact(DisplayName = "DataSize.Zero should produce the expected result")]
+ public void DataSizeZeroShouldProduceExpectedResult()
+ {
+ // Given / When
+ DataSize size = DataSize.Zero;
+
+ // Then
+ Assert.Equal(0.0, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(8.0, 8.0)]
+ [InlineData(1000.0, 1000.0)]
+ [InlineData(1024.0, 1024.0)]
+ [InlineData(8192.0, 8192.0)]
+ public void DataSizeFromBitsShouldProduceExpectedBits(double bits, double expectedBits)
+ {
+ // When
+ DataSize size = DataSize.FromBits(bits);
+
+ // Then
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 8.0)]
+ [InlineData(1000.0, 8000.0)]
+ [InlineData(1024.0, 8192.0)]
+ [InlineData(2048.0, 16384.0)]
+ public void DataSizeFromBytesShouldProduceExpectedBits(double bytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromBytes(bytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromKibiBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0)]
+ [InlineData(2.0, 2048.0)]
+ [InlineData(8.0, 8192.0)]
+ [InlineData(10.0, 10240.0)]
+ public void DataSizeFromKibiBitsShouldProduceExpectedBits(double kibibits, double expectedBits)
+ {
+ DataSize size = DataSize.FromKibiBits(kibibits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromKibiBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 8192.0)]
+ [InlineData(2.0, 16384.0)]
+ [InlineData(10.0, 81920.0)]
+ [InlineData(0.5, 4096.0)]
+ public void DataSizeFromKibiBytesShouldProduceExpectedBits(double kibibytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromKibiBytes(kibibytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromKiloBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0)]
+ [InlineData(8.0, 8000.0)]
+ [InlineData(10.0, 10000.0)]
+ [InlineData(0.5, 500.0)]
+ public void DataSizeFromKiloBitsShouldProduceExpectedBits(double kilobits, double expectedBits)
+ {
+ DataSize size = DataSize.FromKiloBits(kilobits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromKiloBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 8000.0)]
+ [InlineData(2.0, 16000.0)]
+ [InlineData(0.5, 4000.0)]
+ [InlineData(10.0, 80000.0)]
+ public void DataSizeFromKiloBytesShouldProduceExpectedBits(double kilobytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromKiloBytes(kilobytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromMebiBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0)]
+ public void DataSizeFromMebiBitsShouldProduceExpectedBits(double mebibits, double expectedBits)
+ {
+ DataSize size = DataSize.FromMebiBits(mebibits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromMebiBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 8.0)]
+ public void DataSizeFromMebiBytesShouldProduceExpectedBits(double mebibytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromMebiBytes(mebibytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromMegaBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0)]
+ public void DataSizeFromMegaBitsShouldProduceExpectedBits(double megabits, double expectedBits)
+ {
+ DataSize size = DataSize.FromMegaBits(megabits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromMegaBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 8.0)]
+ public void DataSizeFromMegaBytesShouldProduceExpectedBits(double megabytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromMegaBytes(megabytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromGibiBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0)]
+ public void DataSizeFromGibiBitsShouldProduceExpectedBits(double gibibits, double expectedBits)
+ {
+ DataSize size = DataSize.FromGibiBits(gibibits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromGibiBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ public void DataSizeFromGibiBytesShouldProduceExpectedBits(double gibibytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromGibiBytes(gibibytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromGigaBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0)]
+ public void DataSizeFromGigaBitsShouldProduceExpectedBits(double gigabits, double expectedBits)
+ {
+ DataSize size = DataSize.FromGigaBits(gigabits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromGigaBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ public void DataSizeFromGigaBytesShouldProduceExpectedBits(double gigabytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromGigaBytes(gigabytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromTebiBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ public void DataSizeFromTebiBitsShouldProduceExpectedBits(double tebibits, double expectedBits)
+ {
+ DataSize size = DataSize.FromTebiBits(tebibits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromTebiBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ public void DataSizeFromTebiBytesShouldProduceExpectedBits(double tebibytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromTebiBytes(tebibytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromTeraBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ public void DataSizeFromTeraBitsShouldProduceExpectedBits(double terabits, double expectedBits)
+ {
+ DataSize size = DataSize.FromTeraBits(terabits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromTeraBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ public void DataSizeFromTeraBytesShouldProduceExpectedBits(double terabytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromTeraBytes(terabytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromPebiBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ public void DataSizeFromPebiBitsShouldProduceExpectedBits(double pebibits, double expectedBits)
+ {
+ DataSize size = DataSize.FromPebiBits(pebibits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromPebiBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ public void DataSizeFromPebiBytesShouldProduceExpectedBits(double pebibytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromPebiBytes(pebibytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromPetaBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ public void DataSizeFromPetaBitsShouldProduceExpectedBits(double petabits, double expectedBits)
+ {
+ DataSize size = DataSize.FromPetaBits(petabits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromPetaBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ public void DataSizeFromPetaBytesShouldProduceExpectedBits(double petabytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromPetaBytes(petabytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromExbiBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ public void DataSizeFromExbiBitsShouldProduceExpectedBits(double exbibits, double expectedBits)
+ {
+ DataSize size = DataSize.FromExbiBits(exbibits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromExbiBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ public void DataSizeFromExbiBytesShouldProduceExpectedBits(double exbibytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromExbiBytes(exbibytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromExaBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ public void DataSizeFromExaBitsShouldProduceExpectedBits(double exabits, double expectedBits)
+ {
+ DataSize size = DataSize.FromExaBits(exabits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromExaBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ public void DataSizeFromExaBytesShouldProduceExpectedBits(double exabytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromExaBytes(exabytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromZebiBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ public void DataSizeFromZebiBitsShouldProduceExpectedBits(double zebibits, double expectedBits)
+ {
+ DataSize size = DataSize.FromZebiBits(zebibits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromZebiBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ public void DataSizeFromZebiBytesShouldProduceExpectedBits(double zebibytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromZebiBytes(zebibytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromZettaBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ public void DataSizeFromZettaBitsShouldProduceExpectedBits(double zettabits, double expectedBits)
+ {
+ DataSize size = DataSize.FromZettaBits(zettabits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromZettaBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ public void DataSizeFromZettaBytesShouldProduceExpectedBits(double zettabytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromZettaBytes(zettabytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromYobiBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)]
+ public void DataSizeFromYobiBitsShouldProduceExpectedBits(double yobibits, double expectedBits)
+ {
+ DataSize size = DataSize.FromYobiBits(yobibits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromYobiBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ [InlineData(10.0, 10.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 8.0)]
+ public void DataSizeFromYobiBytesShouldProduceExpectedBits(double yobibytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromYobiBytes(yobibytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromYottaBits should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0)]
+ public void DataSizeFromYottaBitsShouldProduceExpectedBits(double yottabits, double expectedBits)
+ {
+ DataSize size = DataSize.FromYottaBits(yottabits);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Theory(DisplayName = "DataSize.FromYottaBytes should produce the expected Bits")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(0.5, 0.5 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ [InlineData(2.0, 2.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 8.0)]
+ public void DataSizeFromYottaBytesShouldProduceExpectedBits(double yottabytes, double expectedBits)
+ {
+ DataSize size = DataSize.FromYottaBytes(yottabytes);
+ Assert.Equal(expectedBits, size.Bits, Tolerance);
+ }
+
+ [Fact(DisplayName = "DataSize.Add should produce the expected result")]
+ public void DataSizeAddShouldProduceExpectedValue()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(1500.0);
+ DataSize right = DataSize.FromBytes(500.0);
+
+ // When
+ DataSize result = left.Add(right);
+
+ // Then
+ Assert.Equal(2000.0, result.Bytes, Tolerance);
+ }
+
+ [Fact(DisplayName = "DataSize.Subtract should produce the expected result")]
+ public void DataSizeSubtractShouldProduceExpectedValue()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(1500.0);
+ DataSize right = DataSize.FromBytes(400.0);
+
+ // When
+ DataSize result = left.Subtract(right);
+
+ // Then
+ Assert.Equal(1100.0, result.Bytes, Tolerance);
+ }
+
+ [Fact(DisplayName = "DataSize.Multiply should produce the expected result")]
+ public void DataSizeMultiplyShouldProduceExpectedValue()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(10.0); // 80 bits
+ DataSize right = DataSize.FromBytes(3.0); // 24 bits
+
+ // When
+ DataSize result = left.Multiply(right); // 80 * 24 = 1920 bits
+
+ // Then
+ Assert.Equal(1920.0, result.Bits, Tolerance);
+ Assert.Equal(240.0, result.Bytes, Tolerance);
+ }
+
+ [Fact(DisplayName = "DataSize.Divide should produce the expected result")]
+ public void DataSizeDivideShouldProduceExpectedValue()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(100.0); // 800 bits
+ DataSize right = DataSize.FromBytes(20.0); // 160 bits
+
+ // When
+ DataSize result = left.Divide(right); // 800 / 160 = 5 bits
+
+ // Then
+ Assert.Equal(5.0, result.Bits, Tolerance);
+ Assert.Equal(0.625, result.Bytes, Tolerance);
+ }
+
+ [Fact(DisplayName = "DataSize comparison should produce the expected result (left equal to right)")]
+ public void DataSizeComparisonShouldProduceExpectedResultLeftEqualToRight()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(1234.0);
+ DataSize right = DataSize.FromBytes(1234.0);
+
+ // When / Then
+ Assert.Equal(0, DataSize.Compare(left, right));
+ Assert.Equal(0, left.CompareTo(right));
+ Assert.Equal(0, left.CompareTo((object)right));
+ Assert.False(left > right);
+ Assert.True(left >= right);
+ Assert.False(left < right);
+ Assert.True(left <= right);
+ }
+
+ [Fact(DisplayName = "DataSize comparison should produce the expected result (left greater than right)")]
+ public void DataSizeComparisonShouldProduceExpectedLeftGreaterThanRight()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(4567.0);
+ DataSize right = DataSize.FromBytes(1234.0);
+
+ // When / Then
+ Assert.Equal(1, DataSize.Compare(left, right));
+ Assert.Equal(1, left.CompareTo(right));
+ Assert.Equal(1, left.CompareTo((object)right));
+ Assert.True(left > right);
+ Assert.True(left >= right);
+ Assert.False(left < right);
+ Assert.False(left <= right);
+ }
+
+ [Fact(DisplayName = "DataSize comparison should produce the expected result (left less than right)")]
+ public void DataSizeComparisonShouldProduceExpectedLeftLessThanRight()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(1234.0);
+ DataSize right = DataSize.FromBytes(4567.0);
+
+ // When / Then
+ Assert.Equal(-1, DataSize.Compare(left, right));
+ Assert.Equal(-1, left.CompareTo(right));
+ Assert.Equal(-1, left.CompareTo((object)right));
+ Assert.False(left > right);
+ Assert.False(left >= right);
+ Assert.True(left < right);
+ Assert.True(left <= right);
+ }
+
+ [Fact(DisplayName = "DataSize equality should produce the expected result (left equal to right)")]
+ public void DataSizeEqualityShouldProduceExpectedResultLeftEqualToRight()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(2048.0);
+ DataSize right = DataSize.FromBytes(2048.0);
+
+ // When / Then
+ Assert.True(DataSize.Equals(left, right));
+ Assert.True(left.Equals(right));
+ Assert.True(left.Equals((object)right));
+ Assert.True(left == right);
+ Assert.False(left != right);
+ }
+
+ [Fact(DisplayName = "DataSize equality should produce the expected result (left not equal to right)")]
+ public void DataSizeEqualityShouldProduceExpectedResultLeftNotEqualToRight()
+ {
+ // Given
+ DataSize left = DataSize.FromBytes(2048.0);
+ DataSize right = DataSize.FromBytes(4096.0);
+
+ // When / Then
+ Assert.False(DataSize.Equals(left, right));
+ Assert.False(left.Equals(right));
+ Assert.False(left.Equals((object)right));
+ Assert.False(left == right);
+ Assert.True(left != right);
+ }
+
+ [Fact(DisplayName = "DataSize.ToString should produce the expected result")]
+ public void DataSizeToStringShouldProduceExpectedResult()
+ {
+ // Given / When
+ // Use values that render nicely across several specifiers.
+ DataSize size = DataSize.FromBytes(1_048_576.0); // 1 MiB
+
+ // Then (explicit formatting + scale)
+ Assert.Equal("8,388,608.000 b", $"{size:b3}");
+ Assert.Equal("1,048,576.000 B", $"{size:B3}");
+ Assert.Equal("8,192.000 Kib", $"{size:Kib3}");
+ Assert.Equal("1,024.000 KiB", $"{size:KiB3}");
+ Assert.Equal("8,388.608 Kb", $"{size:Kb3}");
+ Assert.Equal("1,048.576 KB", $"{size:KB3}");
+ Assert.Equal("8.000 Mib", $"{size:Mib3}");
+ Assert.Equal("1.000 MiB", $"{size:MiB3}");
+ }
+
+ [Fact(DisplayName = "DataSize.ToString should honor custom culture separators")]
+ public void DataSizeToStringShouldHonorCustomCulture()
+ {
+ // Given
+ CultureInfo customCulture = new("de-DE");
+ DataSize size = DataSize.FromKiloBytes(1234.56); // 1,234.56 KB
+
+ // When
+ string formatted = size.ToString("KB2", customCulture);
+
+ // Then (German uses '.' for thousands and ',' for decimals)
+ Assert.Equal("1.234,56 KB", formatted);
+ }
+}
diff --git a/OnixLabs.Units.UnitTests/DistanceTests.cs b/OnixLabs.Units.UnitTests/DistanceTests.cs
new file mode 100644
index 0000000..78cd326
--- /dev/null
+++ b/OnixLabs.Units.UnitTests/DistanceTests.cs
@@ -0,0 +1,665 @@
+// Copyright 2020-2025 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR 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 OnixLabs.Numerics;
+
+namespace OnixLabs.Units.UnitTests;
+
+public sealed class DistanceTests
+{
+ // IEEE-754 binary floating-point arithmetic causes small discrepancies in calculation, therefore we need a tolerance.
+ private const double Tolerance = 1e+42;
+
+ [Fact(DisplayName = "Distance.Zero should produce the expected result")]
+ public void DistanceZeroShouldProduceExpectedResult()
+ {
+ // Given / When
+ Distance distance = Distance.Zero;
+
+ // Then
+ Assert.Equal(0.0, distance.QuectoMeters, Tolerance);
+ }
+ [Theory(DisplayName = "Distance.FromQuectometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1.0)]
+ [InlineData(2.5, 2.5)]
+ public void DistanceFromQuectometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromQuectometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromRontometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e3)]
+ [InlineData(2.5, 2.5e3)]
+ public void DistanceFromRontometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromRontometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromYoctometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e6)]
+ [InlineData(2.5, 2.5e6)]
+ public void DistanceFromYoctometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromYoctometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromZeptometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e9)]
+ [InlineData(2.5, 2.5e9)]
+ public void DistanceFromZeptometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromZeptometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromAttometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e12)]
+ [InlineData(2.5, 2.5e12)]
+ public void DistanceFromAttometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromAttometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromFemtometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e15)]
+ [InlineData(2.5, 2.5e15)]
+ public void DistanceFromFemtometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromFemtometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromPicometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e18)]
+ [InlineData(2.5, 2.5e18)]
+ public void DistanceFromPicometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromPicometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromNanometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e21)]
+ [InlineData(2.5, 2.5e21)]
+ public void DistanceFromNanometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromNanometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromMicrometers should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e24)]
+ [InlineData(2.5, 2.5e24)]
+ public void DistanceFromMicrometersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromMicrometers(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromMillimeters should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e27)]
+ [InlineData(2.5, 2.5e27)]
+ public void DistanceFromMillimetersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromMillimeters(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromCentimeters should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e28)]
+ [InlineData(2.5, 2.5e28)]
+ public void DistanceFromCentimetersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance d = Distance.FromCentimeters(value);
+
+ // Then
+ Assert.Equal(expectedQuectometers, d.QuectoMeters, Tolerance);
+ }
+
+ [Theory(DisplayName = "Distance.FromDecimeters should produce the expected QuectoMeters")]
+ [InlineData(0.0, 0.0)]
+ [InlineData(1.0, 1e29)]
+ [InlineData(2.5, 2.5e29)]
+ public void DistanceFromDecimetersShouldProduceExpectedQuectoMeters(double value, double expectedQuectometers)
+ {
+ // Given / When
+ Distance