diff --git a/X10D/src/Math/BinaryIntegerExtensions.cs b/X10D/src/Math/BinaryIntegerExtensions.cs
index 94b3728..20eae95 100644
--- a/X10D/src/Math/BinaryIntegerExtensions.cs
+++ b/X10D/src/Math/BinaryIntegerExtensions.cs
@@ -1,6 +1,5 @@
#if NET7_0_OR_GREATER
using System.Diagnostics.Contracts;
-using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using X10D.CompilerServices;
@@ -31,28 +30,5 @@ public static class BinaryIntegerExtensions
TInteger root = TInteger.Abs(value).Mod(nine);
return int.CreateChecked(root == TInteger.Zero ? nine : root);
}
-
- ///
- /// 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.MethodImplOptions)]
- public static TInteger Mod(this TInteger dividend, TInteger divisor)
- where TInteger : IBinaryInteger
- {
- TInteger r = dividend % divisor;
- return r < TInteger.Zero ? r + divisor : r;
- }
}
#endif
diff --git a/X10D/src/Math/MathUtility.cs b/X10D/src/Math/MathUtility.cs
index c5bc859..2ba9f21 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,6 +13,7 @@ public static class MathUtility
private const double DefaultGamma = 2.2;
private const float DefaultGammaF = 2.2f;
+#if !NET7_0_OR_GREATER
///
/// Applies a simple bias function to value.
///
@@ -40,80 +42,6 @@ public static class MathUtility
return value / ((1.0 / bias - 2.0) * (1.0 - value) + 1.0);
}
- ///
- /// Calculates exponential decay for a value.
- ///
- /// The value to decay.
- /// A factor by which to scale the decay.
- /// The decay amount.
- /// The exponentially decayed value.
- public static float ExponentialDecay(float value, float alpha, float decay)
- {
- return value * MathF.Exp(-decay * alpha);
- }
-
- ///
- /// Calculates exponential decay for a value.
- ///
- /// The value to decay.
- /// A factor by which to scale the decay.
- /// The decay amount.
- /// The exponentially decayed value.
- public static double ExponentialDecay(double value, double alpha, double decay)
- {
- return value * System.Math.Exp(-decay * alpha);
- }
-
- ///
- /// Converts a gamma-encoded value to a linear value using a gamma value of 2.2.
- ///
- /// The gamma-encoded value to convert. Expected range is [0, 1].
- /// The linear value.
- [Pure]
- [MethodImpl(CompilerResources.MethodImplOptions)]
- public static float GammaToLinear(float value)
- {
- return GammaToLinear(value, DefaultGammaF);
- }
-
- ///
- /// Converts a gamma-encoded value to a linear value using the specified gamma value.
- ///
- /// The gamma-encoded value to convert. Expected range is [0, 1].
- /// The gamma value to use for decoding.
- /// The linear value.
- [Pure]
- [MethodImpl(CompilerResources.MethodImplOptions)]
- public static float GammaToLinear(float value, float gamma)
- {
- return MathF.Pow(value, 1.0f / gamma);
- }
-
- ///
- /// Converts a gamma-encoded value to a linear value using a gamma value of 2.2.
- ///
- /// The gamma-encoded value to convert. Expected range is [0, 1].
- /// The linear value.
- [Pure]
- [MethodImpl(CompilerResources.MethodImplOptions)]
- public static double GammaToLinear(double value)
- {
- return GammaToLinear(value, DefaultGamma);
- }
-
- ///
- /// Converts a gamma-encoded value to a linear value using the specified gamma value.
- ///
- /// The gamma-encoded value to convert. Expected range is [0, 1].
- /// The gamma value to use for decoding.
- /// The linear value.
- [Pure]
- [MethodImpl(CompilerResources.MethodImplOptions)]
- public static double GammaToLinear(double value, double gamma)
- {
- return System.Math.Pow(value, 1.0 / gamma);
- }
-
///
/// Returns the linear interpolation inverse of a value, such that it determines where a value lies between two other
/// values.
@@ -190,6 +118,167 @@ 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.MethodImplOptions)]
+ 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.MethodImplOptions)]
+ 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
+
+ ///
+ /// Calculates exponential decay for a value.
+ ///
+ /// The value to decay.
+ /// A factor by which to scale the decay.
+ /// The decay amount.
+ /// The exponentially decayed value.
+ public static float ExponentialDecay(float value, float alpha, float decay)
+ {
+ return value * MathF.Exp(-decay * alpha);
+ }
+
+ ///
+ /// Calculates exponential decay for a value.
+ ///
+ /// The value to decay.
+ /// A factor by which to scale the decay.
+ /// The decay amount.
+ /// The exponentially decayed value.
+ public static double ExponentialDecay(double value, double alpha, double decay)
+ {
+ return value * System.Math.Exp(-decay * alpha);
+ }
+
+ ///
+ /// Converts a gamma-encoded value to a linear value using a gamma value of 2.2.
+ ///
+ /// The gamma-encoded value to convert. Expected range is [0, 1].
+ /// The linear value.
+ [Pure]
+ [MethodImpl(CompilerResources.MethodImplOptions)]
+ public static float GammaToLinear(float value)
+ {
+ return GammaToLinear(value, DefaultGammaF);
+ }
+
+ ///
+ /// Converts a gamma-encoded value to a linear value using the specified gamma value.
+ ///
+ /// The gamma-encoded value to convert. Expected range is [0, 1].
+ /// The gamma value to use for decoding.
+ /// The linear value.
+ [Pure]
+ [MethodImpl(CompilerResources.MethodImplOptions)]
+ public static float GammaToLinear(float value, float gamma)
+ {
+ return MathF.Pow(value, 1.0f / gamma);
+ }
+
+ ///
+ /// Converts a gamma-encoded value to a linear value using a gamma value of 2.2.
+ ///
+ /// The gamma-encoded value to convert. Expected range is [0, 1].
+ /// The linear value.
+ [Pure]
+ [MethodImpl(CompilerResources.MethodImplOptions)]
+ public static double GammaToLinear(double value)
+ {
+ return GammaToLinear(value, DefaultGamma);
+ }
+
+ ///
+ /// Converts a gamma-encoded value to a linear value using the specified gamma value.
+ ///
+ /// The gamma-encoded value to convert. Expected range is [0, 1].
+ /// The gamma value to use for decoding.
+ /// The linear value.
+ [Pure]
+ [MethodImpl(CompilerResources.MethodImplOptions)]
+ public static double GammaToLinear(double value, double gamma)
+ {
+ return System.Math.Pow(value, 1.0 / gamma);
+ }
+
///
/// Converts a linear value to a gamma-encoded value using a gamma value of 2.2.
///
@@ -288,64 +377,6 @@ public static class MathUtility
#endif
}
- ///
- /// 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.MethodImplOptions)]
- 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.MethodImplOptions)]
- 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.
///
@@ -374,18 +405,92 @@ 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);
+ }
+
+ ///
+ /// Returns the linear interpolation inverse of a value, such that it determines where a value lies between two other
+ /// values.
+ ///
+ /// The value whose lerp inverse is to be found.
+ /// The start of the range.
+ /// The end of the range.
+ /// A value determined by (alpha - start) / (end - start).
+ [Pure]
+ [MethodImpl(CompilerResources.MethodImplOptions)]
+ public static TNumber InverseLerp(TNumber alpha, TNumber start, TNumber end)
+ where TNumber : INumber
+ {
+ if (start == end)
+ {
+ return TNumber.Zero;
+ }
+
+ return (alpha - start) / (end - start);
+ }
+
+ ///
+ /// 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.MethodImplOptions)]
+ 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.MethodImplOptions)]
+ 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;
}
///
@@ -395,10 +500,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..12f00f8
--- /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.MethodImplOptions)]
+ 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.MethodImplOptions)]
+ 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.MethodImplOptions)]
+ 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.MethodImplOptions)]
+ public static int Sign(this TNumber value)
+ where TNumber : INumber
+ {
+ return TNumber.Sign(value);
+ }
+}
+#endif