From b91aad630593744218c75131b506c534a391b5ad Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Wed, 5 Apr 2023 15:35:25 +0100 Subject: [PATCH] feat: convert DigitalRoot and Mod to generic math --- X10D.Tests/src/Math/UInt32Tests.cs | 6 +++ X10D.Tests/src/Math/UInt64Tests.cs | 8 +++- X10D/src/Math/BigIntegerExtensions.cs | 4 ++ X10D/src/Math/BinaryIntegerExtensions.cs | 58 ++++++++++++++++++++++++ X10D/src/Math/ByteExtensions.cs | 2 + X10D/src/Math/Int16Extensions.cs | 4 ++ X10D/src/Math/Int32Extensions.cs | 4 ++ X10D/src/Math/Int64Extensions.cs | 4 ++ X10D/src/Math/SByteExtensions.cs | 4 ++ X10D/src/Math/UInt16Extensions.cs | 2 + X10D/src/Math/UInt32Extensions.cs | 2 + X10D/src/Math/UInt64Extensions.cs | 2 + X10D/src/Time/Int16Extensions.cs | 2 +- X10D/src/Time/SByteExtensions.cs | 2 +- 14 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 X10D/src/Math/BinaryIntegerExtensions.cs diff --git a/X10D.Tests/src/Math/UInt32Tests.cs b/X10D.Tests/src/Math/UInt32Tests.cs index 4454642..0ea052b 100644 --- a/X10D.Tests/src/Math/UInt32Tests.cs +++ b/X10D.Tests/src/Math/UInt32Tests.cs @@ -11,8 +11,14 @@ public partial class UInt32Tests public void DigitalRootShouldBeCorrect() { const uint value = 238; + +#if NET7_0_OR_GREATER + Assert.AreEqual(4, value.DigitalRoot()); + Assert.AreEqual(4, (-value).DigitalRoot()); +#else Assert.AreEqual(4U, value.DigitalRoot()); Assert.AreEqual(4U, (-value).DigitalRoot()); +#endif } [TestMethod] diff --git a/X10D.Tests/src/Math/UInt64Tests.cs b/X10D.Tests/src/Math/UInt64Tests.cs index c5e81ba..17f60f4 100644 --- a/X10D.Tests/src/Math/UInt64Tests.cs +++ b/X10D.Tests/src/Math/UInt64Tests.cs @@ -11,12 +11,18 @@ public partial class UInt64Tests public void DigitalRootShouldBeCorrect() { const ulong value = 238; - Assert.AreEqual(4U, value.DigitalRoot()); // -ulong operator not defined because it might exceed long.MinValue, // so instead, cast to long and then negate. // HAX. + +#if NET7_0_OR_GREATER + Assert.AreEqual(4, (-(long)value).DigitalRoot()); + Assert.AreEqual(4, value.DigitalRoot()); +#else Assert.AreEqual(4U, (-(long)value).DigitalRoot()); + Assert.AreEqual(4U, value.DigitalRoot()); +#endif } [TestMethod] diff --git a/X10D/src/Math/BigIntegerExtensions.cs b/X10D/src/Math/BigIntegerExtensions.cs index 4ce5a0d..ead10ca 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 /// /// Computes the digital root of this 8-bit integer. /// @@ -27,6 +28,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. @@ -155,6 +157,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. /// @@ -176,6 +179,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..94b3728 --- /dev/null +++ b/X10D/src/Math/BinaryIntegerExtensions.cs @@ -0,0 +1,58 @@ +#if NET7_0_OR_GREATER +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Numerics; +using System.Runtime.CompilerServices; +using X10D.CompilerServices; + +namespace X10D.Math; + +/// +/// Math-related extension methods for . +/// +public static class BinaryIntegerExtensions +{ + /// + /// 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.MethodImplOptions)] + 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); + } + + /// + /// 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/ByteExtensions.cs b/X10D/src/Math/ByteExtensions.cs index 6a7db33..0d4ea56 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 /// /// Computes the digital root of this 8-bit integer. /// @@ -26,6 +27,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 7896cf3..e4e57ca 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 /// /// Computes the digital root of this 16-bit integer. /// @@ -25,6 +26,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. @@ -125,6 +127,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. /// @@ -146,6 +149,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 edaf0dd..71df5a3 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 /// /// Computes the digital root of this 32-bit integer. /// @@ -25,6 +26,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. @@ -125,6 +127,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. /// @@ -146,6 +149,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 5f888cc..85405fc 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 /// /// Computes the digital root of this 64-bit integer. /// @@ -25,6 +26,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. @@ -164,6 +166,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. /// @@ -185,6 +188,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/SByteExtensions.cs b/X10D/src/Math/SByteExtensions.cs index cc61037..81b827a 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 /// /// Computes the digital root of this 32-bit integer. /// @@ -26,6 +27,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. @@ -126,6 +128,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. /// @@ -147,6 +150,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 e118e58..95f60e6 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 /// /// Computes the digital root of the current 16-bit unsigned integer. /// @@ -26,6 +27,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 79fed45..5661274 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 /// /// Computes the digital root of the current 32-bit unsigned integer. /// @@ -26,6 +27,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 4bf9365..64cb2ce 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 /// /// Computes the digital root of the current 64-bit unsigned integer. /// @@ -26,6 +27,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 d1644df..9673aa9 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 7013e93..dccf0a0 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 } ///