diff --git a/X10D.Tests/src/IO/UInt16Tests.cs b/X10D.Tests/src/IO/UInt16Tests.cs
index 465a1e0..be9360e 100644
--- a/X10D.Tests/src/IO/UInt16Tests.cs
+++ b/X10D.Tests/src/IO/UInt16Tests.cs
@@ -1,4 +1,4 @@
-using NUnit.Framework;
+using NUnit.Framework;
using X10D.IO;
namespace X10D.Tests.IO;
diff --git a/X10D.Tests/src/IO/UInt32Tests.cs b/X10D.Tests/src/IO/UInt32Tests.cs
index cb0f17b..7847759 100644
--- a/X10D.Tests/src/IO/UInt32Tests.cs
+++ b/X10D.Tests/src/IO/UInt32Tests.cs
@@ -1,4 +1,4 @@
-using NUnit.Framework;
+using NUnit.Framework;
using X10D.IO;
namespace X10D.Tests.IO;
diff --git a/X10D.Tests/src/IO/UInt64Tests.cs b/X10D.Tests/src/IO/UInt64Tests.cs
index dfaa621..e783e2a 100644
--- a/X10D.Tests/src/IO/UInt64Tests.cs
+++ b/X10D.Tests/src/IO/UInt64Tests.cs
@@ -1,4 +1,4 @@
-using NUnit.Framework;
+using NUnit.Framework;
using X10D.IO;
namespace X10D.Tests.IO;
diff --git a/X10D.Tests/src/Math/UInt32Tests.cs b/X10D.Tests/src/Math/UInt32Tests.cs
index 5c161ff..f2db8e8 100644
--- a/X10D.Tests/src/Math/UInt32Tests.cs
+++ b/X10D.Tests/src/Math/UInt32Tests.cs
@@ -1,4 +1,4 @@
-using NUnit.Framework;
+using NUnit.Framework;
using X10D.Math;
namespace X10D.Tests.Math;
diff --git a/X10D.Tests/src/Math/UInt64Tests.cs b/X10D.Tests/src/Math/UInt64Tests.cs
index 909548f..7391cc5 100644
--- a/X10D.Tests/src/Math/UInt64Tests.cs
+++ b/X10D.Tests/src/Math/UInt64Tests.cs
@@ -1,4 +1,4 @@
-using NUnit.Framework;
+using NUnit.Framework;
using X10D.Math;
namespace X10D.Tests.Math;
diff --git a/X10D/src/Collections/BinaryIntegerExtensions.cs b/X10D/src/Collections/BinaryIntegerExtensions.cs
new file mode 100644
index 0000000..7c68d88
--- /dev/null
+++ b/X10D/src/Collections/BinaryIntegerExtensions.cs
@@ -0,0 +1,89 @@
+#if NET7_0_OR_GREATER
+using System.Diagnostics.Contracts;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics.X86;
+using X10D.CompilerServices;
+
+namespace X10D.Collections;
+
+///
+/// Collection-related extension methods for .
+///
+public static class BinaryIntegerExtensions
+{
+ ///
+ /// Unpacks this integer into a boolean list, treating it as a bit field.
+ ///
+ /// The value to unpack.
+ /// An array of with a length equal to the size of .
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static bool[] Unpack(this TInteger value)
+ where TInteger : unmanaged, IBinaryInteger
+ {
+ unsafe
+ {
+ var buffer = new bool[sizeof(TInteger) * 8];
+ value.Unpack(buffer);
+ return buffer;
+ }
+ }
+
+ ///
+ /// Unpacks this integer into a boolean list, treating it as a bit field.
+ ///
+ /// The value to unpack.
+ /// When this method returns, contains the unpacked booleans from .
+ /// is not large enough to contain the result.
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static void Unpack(this TInteger value, Span destination)
+ where TInteger : unmanaged, IBinaryInteger
+ {
+ unsafe
+ {
+ if (destination.Length < sizeof(TInteger) * 8)
+ {
+ throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination));
+ }
+ }
+
+ switch (value)
+ {
+ case byte valueByte when Sse3.IsSupported:
+ valueByte.UnpackInternal_Ssse3(destination);
+ break;
+
+ case int valueInt32 when Avx2.IsSupported:
+ valueInt32.UnpackInternal_Ssse3(destination);
+ break;
+
+ case int valueInt32 when Sse3.IsSupported:
+ valueInt32.UnpackInternal_Ssse3(destination);
+ break;
+
+ case short valueInt16 when Sse3.IsSupported:
+ valueInt16.UnpackInternal_Ssse3(destination);
+ break;
+
+ default:
+ UnpackInternal_Fallback(value, destination);
+ break;
+ }
+ }
+
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ internal static void UnpackInternal_Fallback(this TInteger value, Span destination)
+ where TInteger : unmanaged, IBinaryInteger
+ {
+ unsafe
+ {
+ int bitCount = sizeof(TInteger) * 8;
+ for (var index = 0; index < bitCount; index++)
+ {
+ destination[index] = (value & (TInteger.One << index)) != TInteger.Zero;
+ }
+ }
+ }
+}
+#endif
diff --git a/X10D/src/Collections/ByteExtensions.cs b/X10D/src/Collections/ByteExtensions.cs
index 5207b26..bdcdcce 100644
--- a/X10D/src/Collections/ByteExtensions.cs
+++ b/X10D/src/Collections/ByteExtensions.cs
@@ -1,5 +1,7 @@
+#if !NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+#endif
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@@ -14,6 +16,7 @@ public static class ByteExtensions
{
private const int Size = sizeof(byte) * 8;
+#if !NET7_0_OR_GREATER
///
/// Unpacks this 8-bit unsigned integer into a boolean list, treating it as a bit field.
///
@@ -51,6 +54,7 @@ public static class ByteExtensions
UnpackInternal_Fallback(value, destination);
}
+#endif
[MethodImpl(CompilerResources.MaxOptimization)]
internal static void UnpackInternal_Fallback(this byte value, Span destination)
diff --git a/X10D/src/Collections/Int16Extensions.cs b/X10D/src/Collections/Int16Extensions.cs
index fb722b9..e3e5570 100644
--- a/X10D/src/Collections/Int16Extensions.cs
+++ b/X10D/src/Collections/Int16Extensions.cs
@@ -1,5 +1,7 @@
+#if !NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+#endif
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@@ -14,6 +16,7 @@ public static class Int16Extensions
{
private const int Size = sizeof(short) * 8;
+#if !NET7_0_OR_GREATER
///
/// Unpacks this 16-bit signed integer into a boolean list, treating it as a bit field.
///
@@ -51,6 +54,7 @@ public static class Int16Extensions
UnpackInternal_Fallback(value, destination);
}
+#endif
[MethodImpl(CompilerResources.MaxOptimization)]
internal static void UnpackInternal_Fallback(this short value, Span destination)
diff --git a/X10D/src/Collections/Int32Extensions.cs b/X10D/src/Collections/Int32Extensions.cs
index 19dbe7e..b74a311 100644
--- a/X10D/src/Collections/Int32Extensions.cs
+++ b/X10D/src/Collections/Int32Extensions.cs
@@ -1,5 +1,7 @@
+#if !NET7_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+#endif
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@@ -14,6 +16,7 @@ public static class Int32Extensions
{
private const int Size = sizeof(int) * 8;
+#if !NET7_0_OR_GREATER
///
/// Unpacks this 32-bit signed integer into a boolean list, treating it as a bit field.
///
@@ -57,6 +60,7 @@ public static class Int32Extensions
UnpackInternal_Fallback(value, destination);
}
+#endif
[MethodImpl(CompilerResources.MaxOptimization)]
internal static void UnpackInternal_Fallback(this int value, Span destination)
diff --git a/X10D/src/Collections/Int64Extensions.cs b/X10D/src/Collections/Int64Extensions.cs
index 505f3da..c72dfbd 100644
--- a/X10D/src/Collections/Int64Extensions.cs
+++ b/X10D/src/Collections/Int64Extensions.cs
@@ -1,3 +1,4 @@
+#if !NET7_0_OR_GREATER
using System.Diagnostics.Contracts;
namespace X10D.Collections;
@@ -41,3 +42,4 @@ public static class Int64Extensions
}
}
}
+#endif
diff --git a/X10D/src/IO/DoubleExtensions.cs b/X10D/src/IO/DoubleExtensions.cs
index c190e45..8856c79 100644
--- a/X10D/src/IO/DoubleExtensions.cs
+++ b/X10D/src/IO/DoubleExtensions.cs
@@ -1,4 +1,4 @@
-using System.Buffers.Binary;
+using System.Buffers.Binary;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
diff --git a/X10D/src/IO/Int16Extensions.cs b/X10D/src/IO/Int16Extensions.cs
index f5840e2..af81b2d 100644
--- a/X10D/src/IO/Int16Extensions.cs
+++ b/X10D/src/IO/Int16Extensions.cs
@@ -1,4 +1,4 @@
-using System.Buffers.Binary;
+using System.Buffers.Binary;
using System.Diagnostics.Contracts;
namespace X10D.IO;
diff --git a/X10D/src/IO/Int32Extensions.cs b/X10D/src/IO/Int32Extensions.cs
index 76449f8..0556ab6 100644
--- a/X10D/src/IO/Int32Extensions.cs
+++ b/X10D/src/IO/Int32Extensions.cs
@@ -1,4 +1,4 @@
-using System.Buffers.Binary;
+using System.Buffers.Binary;
using System.Diagnostics.Contracts;
namespace X10D.IO;
diff --git a/X10D/src/IO/Int64Extensions.cs b/X10D/src/IO/Int64Extensions.cs
index 133f86a..447c794 100644
--- a/X10D/src/IO/Int64Extensions.cs
+++ b/X10D/src/IO/Int64Extensions.cs
@@ -1,4 +1,4 @@
-using System.Buffers.Binary;
+using System.Buffers.Binary;
using System.Diagnostics.Contracts;
namespace X10D.IO;
diff --git a/X10D/src/IO/SingleExtensions.cs b/X10D/src/IO/SingleExtensions.cs
index efdb17b..6e65001 100644
--- a/X10D/src/IO/SingleExtensions.cs
+++ b/X10D/src/IO/SingleExtensions.cs
@@ -1,4 +1,4 @@
-using System.Buffers.Binary;
+using System.Buffers.Binary;
using System.Diagnostics.Contracts;
#if !NET5_0_OR_GREATER
using System.Runtime.InteropServices;
diff --git a/X10D/src/IO/UInt16Extensions.cs b/X10D/src/IO/UInt16Extensions.cs
index a977277..972281a 100644
--- a/X10D/src/IO/UInt16Extensions.cs
+++ b/X10D/src/IO/UInt16Extensions.cs
@@ -1,4 +1,4 @@
-using System.Buffers.Binary;
+using System.Buffers.Binary;
using System.Diagnostics.Contracts;
namespace X10D.IO;
diff --git a/X10D/src/IO/UInt32Extensions.cs b/X10D/src/IO/UInt32Extensions.cs
index 7865ff5..099a87d 100644
--- a/X10D/src/IO/UInt32Extensions.cs
+++ b/X10D/src/IO/UInt32Extensions.cs
@@ -1,4 +1,4 @@
-using System.Buffers.Binary;
+using System.Buffers.Binary;
using System.Diagnostics.Contracts;
namespace X10D.IO;
diff --git a/X10D/src/IO/UInt64Extensions.cs b/X10D/src/IO/UInt64Extensions.cs
index a6adf1c..bd9305c 100644
--- a/X10D/src/IO/UInt64Extensions.cs
+++ b/X10D/src/IO/UInt64Extensions.cs
@@ -1,4 +1,4 @@
-using System.Buffers.Binary;
+using System.Buffers.Binary;
using System.Diagnostics.Contracts;
namespace X10D.IO;
diff --git a/X10D/src/Math/BigIntegerExtensions.cs b/X10D/src/Math/BigIntegerExtensions.cs
index b2ab805..79c936c 100644
--- a/X10D/src/Math/BigIntegerExtensions.cs
+++ b/X10D/src/Math/BigIntegerExtensions.cs
@@ -10,6 +10,7 @@ namespace X10D.Math;
///
public static class BigIntegerExtensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current integer.
///
@@ -42,6 +43,7 @@ public static class BigIntegerExtensions
BigInteger root = BigInteger.Abs(value).Mod(9);
return (int)(root == 0 ? 9 : root);
}
+#endif
///
/// Returns the factorial of the current 64-bit signed integer.
@@ -170,6 +172,7 @@ public static class BigIntegerExtensions
return value * other / value.GreatestCommonFactor(other);
}
+#if !NET7_0_OR_GREATER
///
/// Performs a modulo operation which supports a negative dividend.
///
@@ -191,6 +194,7 @@ public static class BigIntegerExtensions
BigInteger r = dividend % divisor;
return r < 0 ? r + divisor : r;
}
+#endif
///
/// Returns the multiplicative persistence of a specified value.
diff --git a/X10D/src/Math/BinaryIntegerExtensions.cs b/X10D/src/Math/BinaryIntegerExtensions.cs
new file mode 100644
index 0000000..5b460c9
--- /dev/null
+++ b/X10D/src/Math/BinaryIntegerExtensions.cs
@@ -0,0 +1,52 @@
+#if NET7_0_OR_GREATER
+using System.Diagnostics.Contracts;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using X10D.CompilerServices;
+
+namespace X10D.Math;
+
+///
+/// Math-related extension methods for .
+///
+public static class BinaryIntegerExtensions
+{
+ ///
+ /// Returns the number of digits in the current binary integer.
+ ///
+ /// The value whose digit count to compute.
+ /// The number of digits in .
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static int CountDigits(this TInteger value)
+ where TInteger : IBinaryInteger
+ {
+ if (TInteger.IsZero(value))
+ {
+ return 1;
+ }
+
+ return 1 + (int)System.Math.Floor(System.Math.Log10(System.Math.Abs(double.CreateChecked(value))));
+ }
+
+ ///
+ /// Computes the digital root of this integer.
+ ///
+ /// The value whose digital root to compute.
+ /// The digital root of .
+ /// The digital root is defined as the recursive sum of digits until that result is a single digit.
+ ///
+ /// The digital root is defined as the recursive sum of digits until that result is a single digit.
+ /// For example, the digital root of 239 is 5: 2 + 3 + 9 = 14, then 1 + 4 = 5.
+ ///
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static int DigitalRoot(this TInteger value)
+ where TInteger : IBinaryInteger
+ {
+ var nine = TInteger.CreateChecked(9);
+ TInteger root = TInteger.Abs(value).Mod(nine);
+ return int.CreateChecked(root == TInteger.Zero ? nine : root);
+ }
+}
+#endif
diff --git a/X10D/src/Math/ByteExtensions.cs b/X10D/src/Math/ByteExtensions.cs
index 95d1188..500988d 100644
--- a/X10D/src/Math/ByteExtensions.cs
+++ b/X10D/src/Math/ByteExtensions.cs
@@ -9,6 +9,7 @@ namespace X10D.Math;
///
public static class ByteExtensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current 8-bit unsigned integer.
///
@@ -41,6 +42,7 @@ public static class ByteExtensions
int root = value % 9;
return (byte)(root == 0 ? 9 : root);
}
+#endif
///
/// Returns the factorial of the current 8-bit unsigned integer.
diff --git a/X10D/src/Math/Int16Extensions.cs b/X10D/src/Math/Int16Extensions.cs
index aa9fa9b..dc3cf96 100644
--- a/X10D/src/Math/Int16Extensions.cs
+++ b/X10D/src/Math/Int16Extensions.cs
@@ -9,6 +9,7 @@ namespace X10D.Math;
///
public static class Int16Extensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current 16-bit signed integer.
///
@@ -40,6 +41,7 @@ public static class Int16Extensions
short root = System.Math.Abs(value).Mod(9);
return root < 1 ? (short)(9 - root) : root;
}
+#endif
///
/// Returns the factorial of the current 16-bit signed integer.
@@ -140,6 +142,7 @@ public static class Int16Extensions
return (short)((long)value).LowestCommonMultiple(other);
}
+#if !NET7_0_OR_GREATER
///
/// Performs a modulo operation which supports a negative dividend.
///
@@ -161,6 +164,7 @@ public static class Int16Extensions
int r = dividend % divisor;
return (short)(r < 0 ? r + divisor : r);
}
+#endif
///
/// Returns the multiplicative persistence of a specified value.
diff --git a/X10D/src/Math/Int32Extensions.cs b/X10D/src/Math/Int32Extensions.cs
index 4d91fcd..7ed6535 100644
--- a/X10D/src/Math/Int32Extensions.cs
+++ b/X10D/src/Math/Int32Extensions.cs
@@ -9,6 +9,7 @@ namespace X10D.Math;
///
public static class Int32Extensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current 32-bit signed integer.
///
@@ -40,6 +41,7 @@ public static class Int32Extensions
int root = System.Math.Abs(value).Mod(9);
return root < 1 ? 9 - root : root;
}
+#endif
///
/// Returns the factorial of the current 32-bit signed integer.
@@ -140,6 +142,7 @@ public static class Int32Extensions
return (int)((long)value).LowestCommonMultiple(other);
}
+#if !NET7_0_OR_GREATER
///
/// Performs a modulo operation which supports a negative dividend.
///
@@ -161,6 +164,7 @@ public static class Int32Extensions
int r = dividend % divisor;
return r < 0 ? r + divisor : r;
}
+#endif
///
/// Returns the multiplicative persistence of a specified value.
diff --git a/X10D/src/Math/Int64Extensions.cs b/X10D/src/Math/Int64Extensions.cs
index 0c6a530..64b94e0 100644
--- a/X10D/src/Math/Int64Extensions.cs
+++ b/X10D/src/Math/Int64Extensions.cs
@@ -9,6 +9,7 @@ namespace X10D.Math;
///
public static class Int64Extensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current 64-bit signed integer.
///
@@ -40,6 +41,7 @@ public static class Int64Extensions
long root = System.Math.Abs(value).Mod(9L);
return root < 1L ? 9L - root : root;
}
+#endif
///
/// Returns the factorial of the current 64-bit signed integer.
@@ -179,6 +181,7 @@ public static class Int64Extensions
return value * other / value.GreatestCommonFactor(other);
}
+#if !NET7_0_OR_GREATER
///
/// Performs a modulo operation which supports a negative dividend.
///
@@ -200,6 +203,7 @@ public static class Int64Extensions
long r = dividend % divisor;
return r < 0 ? r + divisor : r;
}
+#endif
///
/// Returns the multiplicative persistence of a specified value.
diff --git a/X10D/src/Math/MathUtility.cs b/X10D/src/Math/MathUtility.cs
index 59601ce..0ca9365 100644
--- a/X10D/src/Math/MathUtility.cs
+++ b/X10D/src/Math/MathUtility.cs
@@ -1,4 +1,5 @@
using System.Diagnostics.Contracts;
+using System.Numerics;
using System.Runtime.CompilerServices;
using X10D.CompilerServices;
@@ -12,34 +13,6 @@ public static class MathUtility
private const double DefaultGamma = 2.2;
private const float DefaultGammaF = 2.2f;
- ///
- /// Applies a simple bias function to value.
- ///
- /// The value to which the bias function will be applied.
- /// The bias value. Valid values range from 0-1.
- /// The biased result.
- ///
- /// If is less than 0.5, will be shifted downward; otherwise, upward.
- ///
- public static float Bias(float value, float bias)
- {
- return value / ((1.0f / bias - 2.0f) * (1.0f - value) + 1.0f);
- }
-
- ///
- /// Applies a simple bias function to value.
- ///
- /// The value to which the bias function will be applied.
- /// The bias value. Valid values range from 0-1.
- /// The biased result.
- ///
- /// If is less than 0.5, will be shifted downward; otherwise, upward.
- ///
- public static double Bias(double value, double bias)
- {
- return value / ((1.0 / bias - 2.0) * (1.0 - value) + 1.0);
- }
-
///
/// Calculates exponential decay for a value.
///
@@ -154,6 +127,35 @@ public static class MathUtility
return (alpha - start) / (end - start);
}
+#if !NET7_0_OR_GREATER
+ ///
+ /// Applies a simple bias function to value.
+ ///
+ /// The value to which the bias function will be applied.
+ /// The bias value. Valid values range from 0-1.
+ /// The biased result.
+ ///
+ /// If is less than 0.5, will be shifted downward; otherwise, upward.
+ ///
+ public static float Bias(float value, float bias)
+ {
+ return value / ((1.0f / bias - 2.0f) * (1.0f - value) + 1.0f);
+ }
+
+ ///
+ /// Applies a simple bias function to value.
+ ///
+ /// The value to which the bias function will be applied.
+ /// The bias value. Valid values range from 0-1.
+ /// The biased result.
+ ///
+ /// If is less than 0.5, will be shifted downward; otherwise, upward.
+ ///
+ public static double Bias(double value, double bias)
+ {
+ return value / ((1.0 / bias - 2.0) * (1.0 - value) + 1.0);
+ }
+
///
/// Linearly interpolates from one value to a target using a specified alpha.
///
@@ -190,6 +192,93 @@ public static class MathUtility
return ((1.0 - alpha) * value) + (alpha * target);
}
+ ///
+ /// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
+ ///
+ /// The interpolation source.
+ /// The interpolation target.
+ /// The interpolation alpha.
+ /// The interpolation result.
+ public static float SmoothStep(float value, float target, float alpha)
+ {
+ alpha = System.Math.Clamp(alpha, 0.0f, 1.0f);
+ alpha = -2.0f * alpha * alpha * alpha + 3.0f * alpha * alpha;
+ return target * alpha + value * (1.0f - alpha);
+ }
+
+ ///
+ /// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
+ ///
+ /// The interpolation source.
+ /// The interpolation target.
+ /// The interpolation alpha.
+ /// The interpolation result.
+ public static double SmoothStep(double value, double target, double alpha)
+ {
+ alpha = System.Math.Clamp(alpha, 0.0, 1.0);
+ alpha = -2.0 * alpha * alpha * alpha + 3.0 * alpha * alpha;
+ return target * alpha + value * (1.0 - alpha);
+ }
+
+ ///
+ /// Converts a value from being a percentage of one range, to being the same percentage in a new range.
+ ///
+ /// The value to convert.
+ /// The old minimum value.
+ /// The old maximum value.
+ /// The new minimum value.
+ /// The new maximum value.
+ /// The scaled value.
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static float ScaleRange(float value, float oldMin, float oldMax, float newMin, float newMax)
+ {
+ float oldRange = oldMax - oldMin;
+ float newRange = newMax - newMin;
+ float alpha = (value - oldMin) / oldRange;
+ return (alpha * newRange) + newMin;
+ }
+
+ ///
+ /// Converts a value from being a percentage of one range, to being the same percentage in a new range.
+ ///
+ /// The value to convert.
+ /// The old minimum value.
+ /// The old maximum value.
+ /// The new minimum value.
+ /// The new maximum value.
+ /// The scaled value.
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static double ScaleRange(double value, double oldMin, double oldMax, double newMin, double newMax)
+ {
+ double oldRange = oldMax - oldMin;
+ double newRange = newMax - newMin;
+ double alpha = (value - oldMin) / oldRange;
+ return (alpha * newRange) + newMin;
+ }
+
+ ///
+ /// Returns the incremental sawtooth wave of a given value.
+ ///
+ /// The value to calculate.
+ /// The sawtooth wave of the given value.
+ public static float Sawtooth(float value)
+ {
+ return (value - MathF.Floor(value));
+ }
+
+ ///
+ /// Returns the incremental sawtooth wave of a given value.
+ ///
+ /// The value to calculate.
+ /// The sawtooth wave of the given value.
+ public static double Sawtooth(double value)
+ {
+ return (value - System.Math.Floor(value));
+ }
+#endif
+
///
/// Converts a linear value to a gamma-encoded value using a gamma value of 2.2.
///
@@ -272,64 +361,6 @@ public static class MathUtility
return Unsafe.As(ref result);
}
- ///
- /// Returns the incremental sawtooth wave of a given value.
- ///
- /// The value to calculate.
- /// The sawtooth wave of the given value.
- public static float Sawtooth(float value)
- {
- return (value - MathF.Floor(value));
- }
-
- ///
- /// Returns the incremental sawtooth wave of a given value.
- ///
- /// The value to calculate.
- /// The sawtooth wave of the given value.
- public static double Sawtooth(double value)
- {
- return (value - System.Math.Floor(value));
- }
-
- ///
- /// Converts a value from being a percentage of one range, to being the same percentage in a new range.
- ///
- /// The value to convert.
- /// The old minimum value.
- /// The old maximum value.
- /// The new minimum value.
- /// The new maximum value.
- /// The scaled value.
- [Pure]
- [MethodImpl(CompilerResources.MaxOptimization)]
- public static float ScaleRange(float value, float oldMin, float oldMax, float newMin, float newMax)
- {
- float oldRange = oldMax - oldMin;
- float newRange = newMax - newMin;
- float alpha = (value - oldMin) / oldRange;
- return (alpha * newRange) + newMin;
- }
-
- ///
- /// Converts a value from being a percentage of one range, to being the same percentage in a new range.
- ///
- /// The value to convert.
- /// The old minimum value.
- /// The old maximum value.
- /// The new minimum value.
- /// The new maximum value.
- /// The scaled value.
- [Pure]
- [MethodImpl(CompilerResources.MaxOptimization)]
- public static double ScaleRange(double value, double oldMin, double oldMax, double newMin, double newMax)
- {
- double oldRange = oldMax - oldMin;
- double newRange = newMax - newMin;
- double alpha = (value - oldMin) / oldRange;
- return (alpha * newRange) + newMin;
- }
-
///
/// Calculates the sigmoid function for the given input value.
///
@@ -358,18 +389,71 @@ public static class MathUtility
return 1.0f / (1.0f + System.Math.Exp(-value));
}
+#if NET7_0_OR_GREATER
///
- /// Performs smooth Hermite interpolation from one value to a target using a specified alpha.
+ /// Applies a simple bias function to value.
+ ///
+ /// The value to which the bias function will be applied.
+ /// The bias value. Valid values range from 0-1.
+ /// The biased result.
+ ///
+ /// If is less than 0.5, will be shifted downward; otherwise, upward.
+ ///
+ public static TNumber Bias(TNumber value, TNumber bias)
+ where TNumber : INumber
+ {
+ TNumber identity = TNumber.MultiplicativeIdentity;
+ return value / ((identity / bias - TNumber.CreateChecked(2)) * (identity - value) + identity);
+ }
+
+ ///
+ /// Linearly interpolates from one value to a target using a specified alpha.
///
/// The interpolation source.
/// The interpolation target.
/// The interpolation alpha.
- /// The interpolation result.
- public static float SmoothStep(float value, float target, float alpha)
+ ///
+ /// The interpolation result as determined by (1 - alpha) * value + alpha * target.
+ ///
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static TNumber Lerp(TNumber value, TNumber target, TNumber alpha)
+ where TNumber : INumber
{
- alpha = System.Math.Clamp(alpha, 0.0f, 1.0f);
- alpha = -2.0f * alpha * alpha * alpha + 3.0f * alpha * alpha;
- return target * alpha + value * (1.0f - alpha);
+ // rookie mistake: a + t * (b - a)
+ // "precise" method: (1 - t) * a + t * b
+ return ((TNumber.MultiplicativeIdentity - alpha) * value) + (alpha * target);
+ }
+
+ ///
+ /// Returns the incremental sawtooth wave of a given value.
+ ///
+ /// The value to calculate.
+ /// The sawtooth wave of the given value.
+ public static TNumber Sawtooth(TNumber value)
+ where TNumber : IFloatingPoint
+ {
+ return (value - TNumber.Floor(value));
+ }
+
+ ///
+ /// Converts a value from being a percentage of one range, to being the same percentage in a new range.
+ ///
+ /// The value to convert.
+ /// The old minimum value.
+ /// The old maximum value.
+ /// The new minimum value.
+ /// The new maximum value.
+ /// The scaled value.
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static TNumber ScaleRange(TNumber value, TNumber oldMin, TNumber oldMax, TNumber newMin, TNumber newMax)
+ where TNumber : INumber
+ {
+ TNumber oldRange = oldMax - oldMin;
+ TNumber newRange = newMax - newMin;
+ TNumber alpha = (value - oldMin) / oldRange;
+ return (alpha * newRange) + newMin;
}
///
@@ -379,10 +463,16 @@ public static class MathUtility
/// The interpolation target.
/// The interpolation alpha.
/// The interpolation result.
- public static double SmoothStep(double value, double target, double alpha)
+ public static TNumber SmoothStep(TNumber value, TNumber target, TNumber alpha)
+ where TNumber : INumber
{
- alpha = System.Math.Clamp(alpha, 0.0, 1.0);
- alpha = -2.0 * alpha * alpha * alpha + 3.0 * alpha * alpha;
- return target * alpha + value * (1.0 - alpha);
+ TNumber one = TNumber.One;
+ TNumber two = one + one;
+ TNumber three = two + one;
+
+ alpha = TNumber.Clamp(alpha, TNumber.Zero, TNumber.One);
+ alpha = -two * alpha * alpha * alpha + three * alpha * alpha;
+ return target * alpha + value * (one - alpha);
}
+#endif
}
diff --git a/X10D/src/Math/NumberExtensions.cs b/X10D/src/Math/NumberExtensions.cs
new file mode 100644
index 0000000..3a058c5
--- /dev/null
+++ b/X10D/src/Math/NumberExtensions.cs
@@ -0,0 +1,104 @@
+#if NET7_0_OR_GREATER
+using System.Diagnostics.Contracts;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using X10D.CompilerServices;
+
+namespace X10D.Math;
+
+///
+/// Math-related extension methods for .
+///
+public static class NumberExtensions
+{
+ ///
+ /// Returns a value indicating whether the current value is evenly divisible by 2.
+ ///
+ /// The value whose parity to check.
+ ///
+ /// if is evenly divisible by 2, or
+ /// otherwise.
+ ///
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static bool IsEven(this TNumber value)
+ where TNumber : INumber
+ {
+ return value % TNumber.CreateChecked(2) == TNumber.Zero;
+ }
+
+ ///
+ /// Returns a value indicating whether the current value is not evenly divisible by 2.
+ ///
+ /// The value whose parity to check.
+ ///
+ /// if is not evenly divisible by 2, or
+ /// otherwise.
+ ///
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static bool IsOdd(this TNumber value)
+ where TNumber : INumber
+ {
+ return !value.IsEven();
+ }
+
+ ///
+ /// Performs a modulo operation which supports a negative dividend.
+ ///
+ /// The dividend.
+ /// The divisor.
+ /// The result of dividend mod divisor.
+ ///
+ /// The % operator (commonly called the modulo operator) in C# is not defined to be modulo, but is instead
+ /// remainder. This quirk inherently makes it difficult to use modulo in a negative context, as x % y where x is
+ /// negative will return a negative value, akin to -(x % y), even if precedence is forced. This method provides a
+ /// modulo operation which supports negative dividends.
+ ///
+ /// ShreevatsaR, https://stackoverflow.com/a/1082938/1467293
+ /// CC-BY-SA 2.5
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static TNumber Mod(this TNumber dividend, TNumber divisor)
+ where TNumber : INumber
+ {
+ TNumber r = dividend % divisor;
+ return r < TNumber.Zero ? r + divisor : r;
+ }
+
+ ///
+ /// Returns an integer that indicates the sign of this number.
+ ///
+ /// A signed number.
+ ///
+ /// A number that indicates the sign of , as shown in the following table.
+ ///
+ ///
+ ///
+ /// Return value
+ /// Meaning
+ ///
+ ///
+ /// -
+ /// -1
+ /// is less than zero.
+ ///
+ /// -
+ /// 0
+ /// is equal to zero.
+ ///
+ /// -
+ /// 1
+ /// is greater than zero.
+ ///
+ ///
+ ///
+ [Pure]
+ [MethodImpl(CompilerResources.MaxOptimization)]
+ public static int Sign(this TNumber value)
+ where TNumber : INumber
+ {
+ return TNumber.Sign(value);
+ }
+}
+#endif
diff --git a/X10D/src/Math/SByteExtensions.cs b/X10D/src/Math/SByteExtensions.cs
index a4c18f5..00cfa04 100644
--- a/X10D/src/Math/SByteExtensions.cs
+++ b/X10D/src/Math/SByteExtensions.cs
@@ -10,6 +10,7 @@ namespace X10D.Math;
[CLSCompliant(false)]
public static class SByteExtensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current 8-bit signed integer.
///
@@ -41,6 +42,7 @@ public static class SByteExtensions
int root = System.Math.Abs(value).Mod(9);
return (sbyte)(root < 1 ? 9 - root : root);
}
+#endif
///
/// Returns the factorial of the current 8-bit signed integer.
@@ -141,6 +143,7 @@ public static class SByteExtensions
return (sbyte)((long)value).LowestCommonMultiple(other);
}
+#if !NET7_0_OR_GREATER
///
/// Performs a modulo operation which supports a negative dividend.
///
@@ -162,6 +165,7 @@ public static class SByteExtensions
int r = dividend % divisor;
return (sbyte)(r < 0 ? r + divisor : r);
}
+#endif
///
/// Returns the multiplicative persistence of a specified value.
diff --git a/X10D/src/Math/UInt16Extensions.cs b/X10D/src/Math/UInt16Extensions.cs
index bffe978..8479cb2 100644
--- a/X10D/src/Math/UInt16Extensions.cs
+++ b/X10D/src/Math/UInt16Extensions.cs
@@ -10,6 +10,7 @@ namespace X10D.Math;
[CLSCompliant(false)]
public static class UInt16Extensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current 16-bit signed integer.
///
@@ -41,6 +42,7 @@ public static class UInt16Extensions
var root = (ushort)(value % 9);
return (ushort)(root == 0 ? 9 : root);
}
+#endif
///
/// Returns the factorial of the current 16-bit unsigned integer.
diff --git a/X10D/src/Math/UInt32Extensions.cs b/X10D/src/Math/UInt32Extensions.cs
index 3177425..6684e4f 100644
--- a/X10D/src/Math/UInt32Extensions.cs
+++ b/X10D/src/Math/UInt32Extensions.cs
@@ -10,6 +10,7 @@ namespace X10D.Math;
[CLSCompliant(false)]
public static class UInt32Extensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current 32-bit unsigned integer.
///
@@ -41,6 +42,7 @@ public static class UInt32Extensions
uint root = value % 9;
return root == 0 ? 9 : root;
}
+#endif
///
/// Returns the factorial of the current 32-bit unsigned integer.
diff --git a/X10D/src/Math/UInt64Extensions.cs b/X10D/src/Math/UInt64Extensions.cs
index 029deea..dc1e6f1 100644
--- a/X10D/src/Math/UInt64Extensions.cs
+++ b/X10D/src/Math/UInt64Extensions.cs
@@ -10,6 +10,7 @@ namespace X10D.Math;
[CLSCompliant(false)]
public static class UInt64Extensions
{
+#if !NET7_0_OR_GREATER
///
/// Returns the number of digits in the current 64-bit unsigned integer.
///
@@ -41,6 +42,7 @@ public static class UInt64Extensions
ulong root = value % 9;
return root == 0 ? 9 : root;
}
+#endif
///
/// Returns the factorial of the current 64-bit unsigned integer.
diff --git a/X10D/src/Time/Int16Extensions.cs b/X10D/src/Time/Int16Extensions.cs
index fc0cfdb..0490c47 100644
--- a/X10D/src/Time/Int16Extensions.cs
+++ b/X10D/src/Time/Int16Extensions.cs
@@ -32,7 +32,7 @@ public static class Int16Extensions
value++;
}
- return value.Mod(4) == 0 && (value.Mod(100) != 0 || value.Mod(400) == 0);
+ return value.Mod((short)4) == 0 && (value.Mod((short)100) != 0 || value.Mod((short)400) == 0);
}
///
diff --git a/X10D/src/Time/SByteExtensions.cs b/X10D/src/Time/SByteExtensions.cs
index babd8d3..fea7719 100644
--- a/X10D/src/Time/SByteExtensions.cs
+++ b/X10D/src/Time/SByteExtensions.cs
@@ -33,7 +33,7 @@ public static class SByteExtensions
value++;
}
- return value.Mod(4) == 0 && value.Mod(100) != 0; // mod 400 not required, sbyte.MaxValue is 127 anyway
+ return value.Mod((sbyte)4) == 0 && value.Mod((sbyte)100) != 0; // mod 400 not required, sbyte.MaxValue is 127 anyway
}
///