using System; using System.Collections.Generic; using System.Linq; using QWERTYkez.Mensura.Extensions; using Xunit; namespace QWERTYkez.Mensura.Tests { public class DoubleExtensionsTests { private const double Tolerance = 1e-12; #region Single value conversions (non-nullable) [Theory] [InlineData(0)] [InlineData(1)] [InlineData(255)] public void ToDouble_Byte_ReturnsExpected(byte value) => Assert.Equal(value, value.ToDouble()); [Theory] [InlineData(-128)] [InlineData(0)] [InlineData(127)] public void ToDouble_SByte_ReturnsExpected(sbyte value) => Assert.Equal(value, value.ToDouble()); [Theory] [InlineData(-32768)] [InlineData(0)] [InlineData(32767)] public void ToDouble_Short_ReturnsExpected(short value) => Assert.Equal(value, value.ToDouble()); [Theory] [InlineData(0)] [InlineData(1)] [InlineData(65535)] public void ToDouble_UShort_ReturnsExpected(ushort value) => Assert.Equal(value, value.ToDouble()); [Theory] [InlineData(-2147483648)] [InlineData(0)] [InlineData(2147483647)] public void ToDouble_Int_ReturnsExpected(int value) => Assert.Equal(value, value.ToDouble()); [Theory] [InlineData(0U)] [InlineData(1U)] [InlineData(uint.MaxValue)] public void ToDouble_UInt_ReturnsExpected(uint value) => Assert.Equal(value, value.ToDouble()); [Theory] [InlineData(-9223372036854775808)] [InlineData(0)] [InlineData(9223372036854775807)] public void ToDouble_Long_ReturnsExpected(long value) => Assert.Equal((double)value, value.ToDouble(), Tolerance); [Theory] [InlineData(0UL)] [InlineData(1UL)] [InlineData(ulong.MaxValue)] public void ToDouble_ULong_ReturnsExpected(ulong value) => Assert.Equal((double)value, value.ToDouble(), Tolerance); [Theory] [InlineData(-123456789)] [InlineData(0)] [InlineData(123456789)] public void ToDouble_NInt_ReturnsExpected(nint value) => Assert.Equal(value, value.ToDouble()); [Theory] [InlineData(0u)] [InlineData(123456789u)] public void ToDouble_NUInt_ReturnsExpected(nuint value) => Assert.Equal((double)value, value.ToDouble()); [Theory] [InlineData(0.0f)] [InlineData(123.456f)] [InlineData(-987.654f)] [InlineData(float.Epsilon)] [InlineData(float.MaxValue)] public void ToDouble_Float_ReturnsExpected(float value) => Assert.Equal((double)value, value.ToDouble(), Tolerance); [Theory] [InlineData(0.0)] [InlineData(123.456)] [InlineData(-987.654)] public void ToDouble_Decimal_ReturnsExpected(decimal value) => Assert.Equal((double)value, value.ToDouble(), Tolerance); [Theory] [InlineData(0.0)] [InlineData(123.456)] [InlineData(-987.654)] public void ToDouble_Half_ReturnsExpected(Half value) => Assert.Equal((double)value, value.ToDouble(), Tolerance); #endregion #region Single value conversions (nullable) [Fact] public void ToDouble_NullableByte_Null_ReturnsZero() => Assert.Equal(0.0, ((byte?)null).ToDouble()); [Fact] public void ToDouble_NullableSByte_Null_ReturnsZero() => Assert.Equal(0.0, ((sbyte?)null).ToDouble()); [Fact] public void ToDouble_NullableShort_Null_ReturnsZero() => Assert.Equal(0.0, ((short?)null).ToDouble()); [Fact] public void ToDouble_NullableUShort_Null_ReturnsZero() => Assert.Equal(0.0, ((ushort?)null).ToDouble()); [Fact] public void ToDouble_NullableInt_Null_ReturnsZero() => Assert.Equal(0.0, ((int?)null).ToDouble()); [Fact] public void ToDouble_NullableUInt_Null_ReturnsZero() => Assert.Equal(0.0, ((uint?)null).ToDouble()); [Fact] public void ToDouble_NullableLong_Null_ReturnsZero() => Assert.Equal(0.0, ((long?)null).ToDouble()); [Fact] public void ToDouble_NullableULong_Null_ReturnsZero() => Assert.Equal(0.0, ((ulong?)null).ToDouble()); [Fact] public void ToDouble_NullableNInt_Null_ReturnsZero() => Assert.Equal(0.0, ((nint?)null).ToDouble()); [Fact] public void ToDouble_NullableNUInt_Null_ReturnsZero() => Assert.Equal(0.0, ((nuint?)null).ToDouble()); [Fact] public void ToDouble_NullableFloat_Null_ReturnsZero() => Assert.Equal(0.0, ((float?)null).ToDouble()); [Fact] public void ToDouble_NullableDecimal_Null_ReturnsZero() => Assert.Equal(0.0, ((decimal?)null).ToDouble()); [Fact] public void ToDouble_NullableHalf_Null_ReturnsZero() => Assert.Equal(0.0, ((Half?)null).ToDouble()); #endregion #region NET7_0_OR_GREATER #if NET7_0_OR_GREATER [Theory] [InlineData(0)] [InlineData(1)] [InlineData(-1)] [InlineData(long.MaxValue)] public void ToDouble_Int128_ReturnsExpected(Int128 value) => Assert.Equal((double)value, value.ToDouble(), Tolerance); [Theory] [InlineData(0)] [InlineData(1)] [InlineData(ulong.MaxValue)] public void ToDouble_UInt128_ReturnsExpected(UInt128 value) => Assert.Equal((double)value, value.ToDouble(), Tolerance); [Fact] public void ToDouble_NullableInt128_Null_ReturnsZero() => Assert.Equal(0.0, ((Int128?)null).ToDouble()); [Fact] public void ToDouble_NullableUInt128_Null_ReturnsZero() => Assert.Equal(0.0, ((UInt128?)null).ToDouble()); #endif #endregion #region IEnumerable -> IEnumerable (non-nullable) [Fact] public void ToDouble_IEnumerableByte_ReturnsCorrect() { byte[] source = [1, 2, 3]; var result = source.ToDouble(); Assert.Equal(new double[] { 1, 2, 3 }, result); } [Fact] public void ToDouble_IEnumerableByte_Empty_ReturnsEmpty() { var source = Array.Empty(); var result = source.ToDouble(); Assert.Empty(result); } [Fact] public void ToDouble_IEnumerableByte_Null_Throws() => Assert.Null(((IEnumerable)null!).ToDouble()); [Fact] public void ToDouble_IEnumerableInt_ReturnsCorrect() { int[] source = [-5, 0, 10]; var result = source.ToDouble(); Assert.Equal(new double[] { -5, 0, 10 }, result); } [Fact] public void ToDouble_IEnumerableInt_Null_Throws() => Assert.Null(((IEnumerable)null!).ToDouble()); #endregion #region IEnumerable -> IEnumerable [Fact] public void ToDouble_IEnumerableNullableInt_ReturnsCorrect() { int?[] source = [1, null, 3]; var result = source.ToDouble(); Assert.Equal(3, result.Length); Assert.Equal(1.0, result.First()); Assert.Null(result.Skip(1).First()); Assert.Equal(3.0, result.Last()); } [Fact] public void ToDouble_IEnumerableNullableInt_Null_Throws() => Assert.Null(((IEnumerable)null!).ToDouble()); #endregion #region Array and List conversions (non-nullable) [Fact] public void ToDouble_ByteArray_ReturnsDoubleArray() { byte[] arr = [10, 20]; var result = arr.ToDouble(); Assert.Equal(new double[] { 10, 20 }, result); } [Fact] public void ToDouble_ByteArray_Null_ReturnsNull() => Assert.Null(((byte[])null!).ToDouble()); [Fact] public void ToDouble_ByteList_ReturnsDoubleList() { var list = new List { 10, 20 }; var result = list.ToDouble(); Assert.Equal([10, 20], result); } [Fact] public void ToDouble_ByteList_Null_ReturnsNull() => Assert.Null(((List)null!).ToDouble()); [Fact] public void ToDouble_IntArray_ReturnsDoubleArray() { int[] arr = [-5, 0, 7]; var result = arr.ToDouble(); Assert.Equal(new double[] { -5, 0, 7 }, result); } [Fact] public void ToDouble_IntList_ReturnsDoubleList() { var list = new List { -5, 0, 7 }; var result = list.ToDouble(); Assert.Equal([-5, 0, 7], result); } #endregion #region Array and List conversions (nullable) [Fact] public void ToDouble_NullableIntArray_ReturnsNullableDoubleArray() { int?[] arr = [1, null, 3]; var result = arr.ToDouble(); Assert.Equal(3, result.Length); Assert.Equal(1.0, result[0]); Assert.Null(result[1]); Assert.Equal(3.0, result[2]); } [Fact] public void ToDouble_NullableIntArray_Null_ReturnsNull() => Assert.Null(((int?[])null!).ToDouble()); [Fact] public void ToDouble_NullableIntList_ReturnsNullableDoubleList() { var list = new List { 1, null, 3 }; var result = list.ToDouble(); Assert.Equal(3, result.Count); Assert.Equal(1.0, result[0]); Assert.Null(result[1]); Assert.Equal(3.0, result[2]); } [Fact] public void ToDouble_NullableIntList_Null_ReturnsNull() => Assert.Null(((List)null!).ToDouble()); #endregion #region IReadOnlyCollection -> Span [Fact] public void ToDouble_IReadOnlyCollectionByte_Span_Works() { byte[] source = [1, 2]; Span dest = new double[source.Length]; ((IReadOnlyCollection)source).ToDouble(dest); Assert.Equal(1.0, dest[0]); Assert.Equal(2.0, dest[1]); } [Fact] public void ToDouble_IReadOnlyCollectionByte_EmptySpan_Works() { byte[] source = []; Span dest = []; ((IReadOnlyCollection)source).ToDouble(dest); } [Fact] public void ToDouble_IReadOnlyCollectionByte_DestinationTooShort_Throws() { byte[] source = [1, 2]; Assert.Throws(() => { Span dest = new double[1]; ((IReadOnlyCollection)source).ToDouble(dest); }); } [Fact] public void ToDouble_IReadOnlyCollectionNullableInt_Span_HandlesNulls() { int?[] source = [1, null, 3]; Span dest = new double?[3]; ((IReadOnlyCollection)source).ToDouble(dest); Assert.Equal(1.0, dest[0]); Assert.Null(dest[1]); Assert.Equal(3.0, dest[2]); } #endregion #region Span conversions (ToDoubleCore) - через массивы [Fact] public void IntSpan_ToDouble_ConversionWorks() { int[] source = [1, 2, 3]; var result = source.ToDouble(); Assert.Equal(new double[] { 1, 2, 3 }, result); } [Fact] public void FloatSpan_ToDouble_ConversionWorks() { float[] source = [1.1f, -2.2f]; var result = source.ToDouble(); Assert.Equal(1.1f, result[0], Tolerance); Assert.Equal(-2.2f, result[1], Tolerance); } [Fact] public void NullableDecimalSpan_ToDouble_HandlesNulls() { decimal?[] source = [1.5m, null, -3.3m]; var result = source.ToDouble(); Assert.Equal(3, result.Length); Assert.Equal(1.5, result[0]!.Value, Tolerance); Assert.Null(result[1]); Assert.Equal(-3.3, result[2]!.Value, Tolerance); } #endregion #region Edge cases [Fact] public void ToDouble_LongMaxValue_DoesNotOverflow() { long max = long.MaxValue; double result = max.ToDouble(); Assert.Equal((double)max, result, Tolerance); } [Fact] public void ToDouble_ULongMaxValue_DoesNotOverflow() { ulong max = ulong.MaxValue; double result = max.ToDouble(); Assert.Equal((double)max, result, Tolerance); } [Fact] public void ToDouble_Decimal_HighPrecision_DoesNotCrash() { decimal huge = decimal.MaxValue; var result = huge.ToDouble(); Assert.False(double.IsInfinity(result)); } #endregion } }