From 51260fdfa796a9f2ba24793b6325ea9c1e44bcc1 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 28 Apr 2020 02:31:21 +0300 Subject: [PATCH 01/88] Unsafe pointers replaced with managed pointers --- .../src/System.Runtime.Numerics.csproj | 1 + .../Numerics/BigIntegerCalculator.AddSub.cs | 151 +++++---- .../BigIntegerCalculator.BitsBuffer.cs | 62 ++-- .../Numerics/BigIntegerCalculator.DivRem.cs | 136 ++++---- .../BigIntegerCalculator.FastReducer.cs | 51 +-- .../Numerics/BigIntegerCalculator.SquMul.cs | 293 ++++++++---------- 6 files changed, 348 insertions(+), 346 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj index 9fd7811f61af2..779b89add1e16 100644 --- a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj +++ b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj @@ -29,5 +29,6 @@ + diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index 15cb159db4999..83085f1b86b43 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -3,7 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security; +using System.Runtime.CompilerServices; +using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { @@ -20,45 +21,51 @@ public static uint[] Add(uint[] left, uint right) uint[] bits = new uint[left.Length + 1]; - long digit = (long)left[0] + right; - bits[0] = unchecked((uint)digit); + Add(ref GetArrayDataReference(left), left.Length, + right, + ref GetArrayDataReference(bits)); + + return bits; + } + + private static void Add(ref uint left, int leftLength, + uint right, + ref uint bits) + { + long digit = (long)left + right; + bits = unchecked((uint)digit); long carry = digit >> 32; - for (int i = 1; i < left.Length; i++) + for (int i = 1; i < leftLength; i++) { - digit = left[i] + carry; - bits[i] = unchecked((uint)digit); + digit = Unsafe.Add(ref left, i) + carry; + Unsafe.Add(ref bits, i) = unchecked((uint)digit); carry = digit >> 32; } - bits[left.Length] = (uint)carry; - - return bits; + Unsafe.Add(ref bits, leftLength) = (uint)carry; } - public static unsafe uint[] Add(uint[] left, uint[] right) + public static uint[] Add(uint[] left, uint[] right) { Debug.Assert(left != null); Debug.Assert(right != null); Debug.Assert(left.Length >= right.Length); - // Switching to unsafe pointers helps sparing + // Switching to managed pointers helps sparing // some nasty index calculations... uint[] bits = new uint[left.Length + 1]; - fixed (uint* l = left, r = right, b = &bits[0]) - { - Add(l, left.Length, - r, right.Length, - b, bits.Length); - } + Add(ref GetArrayDataReference(left), left.Length, + ref GetArrayDataReference(right), right.Length, + ref GetArrayDataReference(bits), bits.Length); return bits; } - private static unsafe void Add(uint* left, int leftLength, - uint* right, int rightLength, - uint* bits, int bitsLength) + private static void Add(ref uint left, int leftLength, + ref uint right, int rightLength, + ref uint bits, int bitsLength) { Debug.Assert(leftLength >= 0); Debug.Assert(rightLength >= 0); @@ -75,21 +82,21 @@ private static unsafe void Add(uint* left, int leftLength, for (; i < rightLength; i++) { - long digit = (left[i] + carry) + right[i]; - bits[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref left, i) + carry) + Unsafe.Add(ref right, i); + Unsafe.Add(ref bits, i) = unchecked((uint)digit); carry = digit >> 32; } for (; i < leftLength; i++) { - long digit = left[i] + carry; - bits[i] = unchecked((uint)digit); + long digit = Unsafe.Add(ref left, i) + carry; + Unsafe.Add(ref bits, i) = unchecked((uint)digit); carry = digit >> 32; } - bits[i] = (uint)carry; + Unsafe.Add(ref bits, i) = (uint)carry; } - private static unsafe void AddSelf(uint* left, int leftLength, - uint* right, int rightLength) + private static void AddSelf(ref uint left, int leftLength, + ref uint right, int rightLength) { Debug.Assert(leftLength >= 0); Debug.Assert(rightLength >= 0); @@ -101,17 +108,19 @@ private static unsafe void AddSelf(uint* left, int leftLength, int i = 0; long carry = 0L; - + ref uint leftElement = ref left; for (; i < rightLength; i++) { - long digit = (left[i] + carry) + right[i]; - left[i] = unchecked((uint)digit); + leftElement = ref Unsafe.Add(ref left, i); + long digit = (leftElement + carry) + Unsafe.Add(ref right, i); + leftElement = unchecked((uint)digit); carry = digit >> 32; } for (; carry != 0 && i < leftLength; i++) { - long digit = left[i] + carry; - left[i] = (uint)digit; + leftElement = ref Unsafe.Add(ref left, i); + long digit = leftElement + carry; + leftElement = (uint)digit; carry = digit >> 32; } @@ -130,50 +139,56 @@ public static uint[] Subtract(uint[] left, uint right) uint[] bits = new uint[left.Length]; - long digit = (long)left[0] - right; - bits[0] = unchecked((uint)digit); + Subtract(ref GetArrayDataReference(left), left.Length, + right, + ref GetArrayDataReference(bits)); + + return bits; + } + + private static void Subtract(ref uint left, int leftLength, + uint right, + ref uint bits) + { + long digit = (long)left - right; + bits = unchecked((uint)digit); long carry = digit >> 32; - for (int i = 1; i < left.Length; i++) + for (int i = 1; i < leftLength; i++) { - digit = left[i] + carry; - bits[i] = unchecked((uint)digit); + digit = Unsafe.Add(ref left, i) + carry; + Unsafe.Add(ref bits, i) = unchecked((uint)digit); carry = digit >> 32; } - - return bits; } - public static unsafe uint[] Subtract(uint[] left, uint[] right) + public static uint[] Subtract(uint[] left, uint[] right) { Debug.Assert(left != null); Debug.Assert(right != null); Debug.Assert(left.Length >= right.Length); Debug.Assert(Compare(left, right) >= 0); - // Switching to unsafe pointers helps sparing + // Switching to managed pointers helps sparing // some nasty index calculations... uint[] bits = new uint[left.Length]; - fixed (uint* l = left, r = right, b = bits) - { - Subtract(l, left.Length, - r, right.Length, - b, bits.Length); - } + Subtract(ref GetArrayDataReference(left), left.Length, + ref GetArrayDataReference(right), right.Length, + ref GetArrayDataReference(bits), bits.Length); return bits; } - private static unsafe void Subtract(uint* left, int leftLength, - uint* right, int rightLength, - uint* bits, int bitsLength) + private static void Subtract(ref uint left, int leftLength, + ref uint right, int rightLength, + ref uint bits, int bitsLength) { Debug.Assert(leftLength >= 0); Debug.Assert(rightLength >= 0); Debug.Assert(leftLength >= rightLength); - Debug.Assert(Compare(left, leftLength, right, rightLength) >= 0); + Debug.Assert(Compare(ref left, leftLength, ref right, rightLength) >= 0); Debug.Assert(bitsLength == leftLength); // Executes the "grammar-school" algorithm for computing z = a - b. @@ -186,27 +201,27 @@ private static unsafe void Subtract(uint* left, int leftLength, for (; i < rightLength; i++) { - long digit = (left[i] + carry) - right[i]; - bits[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref left, i) + carry) - Unsafe.Add(ref right, i); + Unsafe.Add(ref bits, i) = unchecked((uint)digit); carry = digit >> 32; } for (; i < leftLength; i++) { - long digit = left[i] + carry; - bits[i] = (uint)digit; + long digit = Unsafe.Add(ref left, i) + carry; + Unsafe.Add(ref bits, i) = (uint)digit; carry = digit >> 32; } Debug.Assert(carry == 0); } - private static unsafe void SubtractSelf(uint* left, int leftLength, - uint* right, int rightLength) + private static void SubtractSelf(ref uint left, int leftLength, + ref uint right, int rightLength) { Debug.Assert(leftLength >= 0); Debug.Assert(rightLength >= 0); Debug.Assert(leftLength >= rightLength); - Debug.Assert(Compare(left, leftLength, right, rightLength) >= 0); + Debug.Assert(Compare(ref left, leftLength, ref right, rightLength) >= 0); // Executes the "grammar-school" algorithm for computing z = a - b. // Same as above, but we're writing the result directly to a and @@ -214,17 +229,19 @@ private static unsafe void SubtractSelf(uint* left, int leftLength, int i = 0; long carry = 0L; - + ref uint leftElement = ref left; for (; i < rightLength; i++) { - long digit = (left[i] + carry) - right[i]; - left[i] = unchecked((uint)digit); + leftElement = ref Unsafe.Add(ref left, i); + long digit = (leftElement + carry) - Unsafe.Add(ref right, i); + leftElement = unchecked((uint)digit); carry = digit >> 32; } for (; carry != 0 && i < leftLength; i++) { - long digit = left[i] + carry; - left[i] = (uint)digit; + leftElement = ref Unsafe.Add(ref left, i); + long digit = leftElement + carry; + leftElement = (uint)digit; carry = digit >> 32; } @@ -252,8 +269,8 @@ public static int Compare(uint[] left, uint[] right) return 0; } - private static unsafe int Compare(uint* left, int leftLength, - uint* right, int rightLength) + private static int Compare(ref uint left, int leftLength, + ref uint right, int rightLength) { Debug.Assert(leftLength >= 0); Debug.Assert(rightLength >= 0); @@ -265,9 +282,9 @@ private static unsafe int Compare(uint* left, int leftLength, for (int i = leftLength - 1; i >= 0; i--) { - if (left[i] < right[i]) + if (Unsafe.Add(ref left, i) < Unsafe.Add(ref right, i)) return -1; - if (left[i] > right[i]) + if (Unsafe.Add(ref left, i) > Unsafe.Add(ref right, i)) return 1; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index 669e096733117..489de32e7c796 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -3,7 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security; +using System.Runtime.CompilerServices; +using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { @@ -44,7 +45,7 @@ public BitsBuffer(int size, uint[] value) Array.Copy(value, _bits, _length); } - public unsafe void MultiplySelf(ref BitsBuffer value, + public void MultiplySelf(ref BitsBuffer value, ref BitsBuffer temp) { Debug.Assert(temp._length == 0); @@ -53,26 +54,27 @@ public unsafe void MultiplySelf(ref BitsBuffer value, // Executes a multiplication for this and value, writes the // result to temp. Switches this and temp arrays afterwards. - fixed (uint* b = _bits, v = value._bits, t = temp._bits) + ref uint b = ref GetArrayDataReference(_bits); + ref uint v = ref GetArrayDataReference(value._bits); + ref uint t = ref GetArrayDataReference(temp._bits); + + if (_length < value._length) + { + Multiply(ref v, value._length, + ref b, _length, + ref t, _length + value._length); + } + else { - if (_length < value._length) - { - Multiply(v, value._length, - b, _length, - t, _length + value._length); - } - else - { - Multiply(b, _length, - v, value._length, - t, _length + value._length); - } + Multiply(ref b, _length, + ref v, value._length, + ref t, _length + value._length); } Apply(ref temp, _length + value._length); } - public unsafe void SquareSelf(ref BitsBuffer temp) + public void SquareSelf(ref BitsBuffer temp) { Debug.Assert(temp._length == 0); Debug.Assert(_length + _length <= temp._bits.Length); @@ -80,11 +82,11 @@ public unsafe void SquareSelf(ref BitsBuffer temp) // Executes a square for this, writes the result to temp. // Switches this and temp arrays afterwards. - fixed (uint* b = _bits, t = temp._bits) - { - Square(b, _length, - t, _length + _length); - } + ref uint b = ref GetArrayDataReference(_bits); + ref uint t = ref GetArrayDataReference(temp._bits); + + Square(ref b, _length, + ref t, _length + _length); Apply(ref temp, _length + _length); } @@ -106,12 +108,9 @@ public unsafe void Reduce(uint[] modulus) if (_length >= modulus.Length) { - fixed (uint* b = _bits, m = modulus) - { - Divide(b, _length, - m, modulus.Length, - null, 0); - } + Divide(ref GetArrayDataReference(_bits), _length, + ref GetArrayDataReference(modulus), modulus.Length, + ref Unsafe.AsRef(null), 0); _length = ActualLength(_bits, modulus.Length); } @@ -124,12 +123,9 @@ public unsafe void Reduce(ref BitsBuffer modulus) if (_length >= modulus._length) { - fixed (uint* b = _bits, m = modulus._bits) - { - Divide(b, _length, - m, modulus._length, - null, 0); - } + Divide(ref GetArrayDataReference(_bits), _length, + ref GetArrayDataReference(modulus._bits), modulus._length, + ref Unsafe.AsRef(null), 0); _length = ActualLength(_bits, modulus._length); } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 78df78eed76b3..dba75021e24a5 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -3,7 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security; +using System.Runtime.CompilerServices; +using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { @@ -21,17 +22,27 @@ public static uint[] Divide(uint[] left, uint right, uint[] quotient = new uint[left.Length]; + Divide(ref GetArrayDataReference(left), left.Length, + right, + ref GetArrayDataReference(quotient), out remainder); + + return quotient; + } + + private static void Divide(ref uint left, int leftLength, + uint right, + ref uint quotient, + out uint remainder) + { ulong carry = 0UL; - for (int i = left.Length - 1; i >= 0; i--) + for (int i = leftLength - 1; i >= 0; i--) { - ulong value = (carry << 32) | left[i]; + ulong value = (carry << 32) | Unsafe.Add(ref left, i); ulong digit = value / right; - quotient[i] = (uint)digit; + Unsafe.Add(ref quotient, i) = (uint)digit; carry = value - digit * right; } remainder = (uint)carry; - - return quotient; } public static uint[] Divide(uint[] left, uint right) @@ -43,16 +54,25 @@ public static uint[] Divide(uint[] left, uint right) uint[] quotient = new uint[left.Length]; + Divide(ref GetArrayDataReference(left), left.Length, + right, + ref GetArrayDataReference(quotient)); + + return quotient; + } + + private static void Divide(ref uint left, int leftLength, + uint right, + ref uint quotient) + { ulong carry = 0UL; - for (int i = left.Length - 1; i >= 0; i--) + for (int i = leftLength - 1; i >= 0; i--) { - ulong value = (carry << 32) | left[i]; + ulong value = (carry << 32) | Unsafe.Add(ref left, i); ulong digit = value / right; - quotient[i] = (uint)digit; + Unsafe.Add(ref quotient, i) = (uint)digit; carry = value - digit * right; } - - return quotient; } public static uint Remainder(uint[] left, uint right) @@ -61,18 +81,23 @@ public static uint Remainder(uint[] left, uint right) Debug.Assert(left.Length >= 1); // Same as above, but only computing the remainder. + return Remainder(ref GetArrayDataReference(left), left.Length, right); + } + private static uint Remainder(ref uint left, int leftLength, + uint right) + { ulong carry = 0UL; - for (int i = left.Length - 1; i >= 0; i--) + for (int i = leftLength - 1; i >= 0; i--) { - ulong value = (carry << 32) | left[i]; + ulong value = (carry << 32) | Unsafe.Add(ref left, i); carry = value % right; } return (uint)carry; } - public static unsafe uint[] Divide(uint[] left, uint[] right, + public static uint[] Divide(uint[] left, uint[] right, out uint[] remainder) { Debug.Assert(left != null); @@ -81,7 +106,7 @@ public static unsafe uint[] Divide(uint[] left, uint[] right, Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); - // Switching to unsafe pointers helps sparing + // Switching to managed pointers helps sparing // some nasty index calculations... // NOTE: left will get overwritten, we need a local copy @@ -89,19 +114,16 @@ public static unsafe uint[] Divide(uint[] left, uint[] right, uint[] localLeft = CreateCopy(left); uint[] bits = new uint[left.Length - right.Length + 1]; - fixed (uint* l = &localLeft[0], r = &right[0], b = &bits[0]) - { - Divide(l, localLeft.Length, - r, right.Length, - b, bits.Length); - } + Divide(ref GetArrayDataReference(localLeft), localLeft.Length, + ref GetArrayDataReference(right), right.Length, + ref GetArrayDataReference(bits), bits.Length); remainder = localLeft; return bits; } - public static unsafe uint[] Divide(uint[] left, uint[] right) + public static uint[] Divide(uint[] left, uint[] right) { Debug.Assert(left != null); Debug.Assert(right != null); @@ -116,12 +138,9 @@ public static unsafe uint[] Divide(uint[] left, uint[] right) uint[] localLeft = CreateCopy(left); uint[] bits = new uint[left.Length - right.Length + 1]; - fixed (uint* l = &localLeft[0], r = &right[0], b = &bits[0]) - { - Divide(l, localLeft.Length, - r, right.Length, - b, bits.Length); - } + Divide(ref GetArrayDataReference(localLeft), localLeft.Length, + ref GetArrayDataReference(right), right.Length, + ref GetArrayDataReference(bits), bits.Length); return bits; } @@ -140,19 +159,16 @@ public static unsafe uint[] Remainder(uint[] left, uint[] right) uint[] localLeft = CreateCopy(left); - fixed (uint* l = &localLeft[0], r = &right[0]) - { - Divide(l, localLeft.Length, - r, right.Length, - null, 0); - } + Divide(ref GetArrayDataReference(localLeft), localLeft.Length, + ref GetArrayDataReference(right), right.Length, + ref Unsafe.AsRef(null), 0); return localLeft; } - private static unsafe void Divide(uint* left, int leftLength, - uint* right, int rightLength, - uint* bits, int bitsLength) + private static void Divide(ref uint left, int leftLength, + ref uint right, int rightLength, + ref uint bits, int bitsLength) { Debug.Assert(leftLength >= 1); Debug.Assert(rightLength >= 1); @@ -165,8 +181,8 @@ private static unsafe void Divide(uint* left, int leftLength, // block of the divisor. Thus, guessing digits of the quotient // will be more precise. Additionally we'll get r = a % b. - uint divHi = right[rightLength - 1]; - uint divLo = rightLength > 1 ? right[rightLength - 2] : 0; + uint divHi = Unsafe.Add(ref right, rightLength - 1); + uint divLo = rightLength > 1 ? Unsafe.Add(ref right, rightLength - 2) : 0; // We measure the leading zeros of the divisor int shift = LeadingZeros(divHi); @@ -175,7 +191,7 @@ private static unsafe void Divide(uint* left, int leftLength, // And, we make sure the most significant bit is set if (shift > 0) { - uint divNx = rightLength > 2 ? right[rightLength - 3] : 0; + uint divNx = rightLength > 2 ? Unsafe.Add(ref right, rightLength - 3) : 0; divHi = (divHi << shift) | (divLo >> backShift); divLo = (divLo << shift) | (divNx >> backShift); @@ -186,15 +202,15 @@ private static unsafe void Divide(uint* left, int leftLength, for (int i = leftLength; i >= rightLength; i--) { int n = i - rightLength; - uint t = i < leftLength ? left[i] : 0; + uint t = i < leftLength ? Unsafe.Add(ref left, i) : 0; - ulong valHi = ((ulong)t << 32) | left[i - 1]; - uint valLo = i > 1 ? left[i - 2] : 0; + ulong valHi = ((ulong)t << 32) | Unsafe.Add(ref left, i - 1); + uint valLo = i > 1 ? Unsafe.Add(ref left, i - 2) : 0; // We shifted the divisor, we shift the dividend too if (shift > 0) { - uint valNx = i > 2 ? left[i - 3] : 0; + uint valNx = i > 2 ? Unsafe.Add(ref left, i - 3) : 0; valHi = (valHi << shift) | (valLo >> backShift); valLo = (valLo << shift) | (valNx >> backShift); @@ -213,15 +229,15 @@ private static unsafe void Divide(uint* left, int leftLength, if (digit > 0) { // Now it's time to subtract our current quotient - uint carry = SubtractDivisor(left + n, leftLength - n, - right, rightLength, digit); + uint carry = SubtractDivisor(ref Unsafe.Add(ref left, n), leftLength - n, + ref right, rightLength, digit); if (carry != t) { Debug.Assert(carry == t + 1); // Our guess was still exactly one too high - carry = AddDivisor(left + n, leftLength - n, - right, rightLength); + carry = AddDivisor(ref Unsafe.Add(ref left, n), leftLength - n, + ref right, rightLength); --digit; Debug.Assert(carry == 1); @@ -230,14 +246,14 @@ private static unsafe void Divide(uint* left, int leftLength, // We have the digit! if (bitsLength != 0) - bits[n] = (uint)digit; + Unsafe.Add(ref bits, n) = (uint)digit; if (i < leftLength) - left[i] = 0; + Unsafe.Add(ref left, i) = 0; } } - private static unsafe uint AddDivisor(uint* left, int leftLength, - uint* right, int rightLength) + private static uint AddDivisor(ref uint left, int leftLength, + ref uint right, int rightLength) { Debug.Assert(leftLength >= 0); Debug.Assert(rightLength >= 0); @@ -249,16 +265,17 @@ private static unsafe uint AddDivisor(uint* left, int leftLength, for (int i = 0; i < rightLength; i++) { - ulong digit = (left[i] + carry) + right[i]; - left[i] = unchecked((uint)digit); + ref uint leftElement = ref Unsafe.Add(ref left, i); + ulong digit = (leftElement + carry) + Unsafe.Add(ref right, i); + leftElement = unchecked((uint)digit); carry = digit >> 32; } return (uint)carry; } - private static unsafe uint SubtractDivisor(uint* left, int leftLength, - uint* right, int rightLength, + private static uint SubtractDivisor(ref uint left, int leftLength, + ref uint right, int rightLength, ulong q) { Debug.Assert(leftLength >= 0); @@ -273,12 +290,13 @@ private static unsafe uint SubtractDivisor(uint* left, int leftLength, for (int i = 0; i < rightLength; i++) { - carry += right[i] * q; + carry += Unsafe.Add(ref right, i) * q; uint digit = unchecked((uint)carry); carry = carry >> 32; - if (left[i] < digit) + ref uint leftElement = ref Unsafe.Add(ref left, i); + if (leftElement < digit) ++carry; - left[i] = unchecked(left[i] - digit); + leftElement = unchecked(leftElement - digit); } return (uint)carry; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 9e7c4ccbd07c7..3af59210b14f7 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -3,7 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security; +using System.Runtime.CompilerServices; +using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { @@ -66,7 +67,7 @@ public int Reduce(uint[] value, int length) _modulus, _modulus.Length + 1); } - private static unsafe int DivMul(uint[] left, int leftLength, + private static int DivMul(uint[] left, int leftLength, uint[] right, int rightLength, uint[] bits, int k) { @@ -88,20 +89,21 @@ private static unsafe int DivMul(uint[] left, int leftLength, { leftLength -= k; - fixed (uint* l = left, r = right, b = bits) + ref uint l = ref GetArrayDataReference(left); + ref uint r = ref GetArrayDataReference(right); + ref uint b = ref GetArrayDataReference(bits); + + if (leftLength < rightLength) + { + Multiply(ref r, rightLength, + ref Unsafe.Add(ref l, k), leftLength, + ref b, leftLength + rightLength); + } + else { - if (leftLength < rightLength) - { - Multiply(r, rightLength, - l + k, leftLength, - b, leftLength + rightLength); - } - else - { - Multiply(l + k, leftLength, - r, rightLength, - b, leftLength + rightLength); - } + Multiply(ref Unsafe.Add(ref l, k), leftLength, + ref r, rightLength, + ref b, leftLength + rightLength); } return ActualLength(bits, leftLength + rightLength); @@ -110,7 +112,7 @@ private static unsafe int DivMul(uint[] left, int leftLength, return 0; } - private static unsafe int SubMod(uint[] left, int leftLength, + private static int SubMod(uint[] left, int leftLength, uint[] right, int rightLength, uint[] modulus, int k) { @@ -129,16 +131,17 @@ private static unsafe int SubMod(uint[] left, int leftLength, if (rightLength > k) rightLength = k; - fixed (uint* l = left, r = right, m = modulus) + ref uint l = ref GetArrayDataReference(left); + ref uint r = ref GetArrayDataReference(right); + ref uint m = ref GetArrayDataReference(modulus); + + SubtractSelf(ref l, leftLength, ref r, rightLength); + leftLength = ActualLength(left, leftLength); + + while (Compare(ref l, leftLength, ref m, modulus.Length) >= 0) { - SubtractSelf(l, leftLength, r, rightLength); + SubtractSelf(ref l, leftLength, ref m, modulus.Length); leftLength = ActualLength(left, leftLength); - - while (Compare(l, leftLength, m, modulus.Length) >= 0) - { - SubtractSelf(l, leftLength, m, modulus.Length); - leftLength = ActualLength(left, leftLength); - } } Array.Clear(left, leftLength, left.Length - leftLength); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index f44ed20a0335a..a44bbb9880848 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -3,13 +3,14 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Security; +using System.Runtime.CompilerServices; +using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static unsafe uint[] Square(uint[] value) + public static uint[] Square(uint[] value) { Debug.Assert(value != null); @@ -18,11 +19,8 @@ public static unsafe uint[] Square(uint[] value) uint[] bits = new uint[value.Length + value.Length]; - fixed (uint* v = value, b = bits) - { - Square(v, value.Length, - b, bits.Length); - } + Square(ref GetArrayDataReference(value), value.Length, + ref GetArrayDataReference(bits), bits.Length); return bits; } @@ -31,8 +29,8 @@ public static unsafe uint[] Square(uint[] value) private static int SquareThreshold = 32; private static int AllocationThreshold = 256; - private static unsafe void Square(uint* value, int valueLength, - uint* bits, int bitsLength) + private static unsafe void Square(ref uint value, int valueLength, + ref uint bits, int bitsLength) { Debug.Assert(valueLength >= 0); Debug.Assert(bitsLength == valueLength + valueLength); @@ -59,20 +57,23 @@ private static unsafe void Square(uint* value, int valueLength, // = 2^64 - 1 (which perfectly matches with ulong!). But // here we would need an UInt65... Hence, we split these // operation and do some extra shifts. - + ref uint elementPtr = ref value; for (int i = 0; i < valueLength; i++) { ulong carry = 0UL; for (int j = 0; j < i; j++) { - ulong digit1 = bits[i + j] + carry; - ulong digit2 = (ulong)value[j] * value[i]; - bits[i + j] = unchecked((uint)(digit1 + (digit2 << 1))); + elementPtr = ref Unsafe.Add(ref bits, i + j); + ulong digit1 = elementPtr + carry; + ulong digit2 = (ulong)Unsafe.Add(ref value, j) * Unsafe.Add(ref value, i); + elementPtr = unchecked((uint)(digit1 + (digit2 << 1))); carry = (digit2 + (digit1 >> 1)) >> 31; } - ulong digits = (ulong)value[i] * value[i] + carry; - bits[i + i] = unchecked((uint)digits); - bits[i + i + 1] = (uint)(digits >> 32); + elementPtr = ref Unsafe.Add(ref value, i); + ulong digits = (ulong)elementPtr * elementPtr + carry; + elementPtr = ref Unsafe.Add(ref bits, i + i); + elementPtr = unchecked((uint)digits); + Unsafe.Add(ref elementPtr, 1) = (uint)(digits >> 32); } } else @@ -93,71 +94,52 @@ private static unsafe void Square(uint* value, int valueLength, int n2 = n << 1; // ... split value like a = (a_1 << n) + a_0 - uint* valueLow = value; + ref uint valueLow = ref value; int valueLowLength = n; - uint* valueHigh = value + n; + ref uint valueHigh = ref Unsafe.Add(ref value, n); int valueHighLength = valueLength - n; // ... prepare our result array (to reuse its memory) - uint* bitsLow = bits; + ref uint bitsLow = ref bits; int bitsLowLength = n2; - uint* bitsHigh = bits + n2; + ref uint bitsHigh = ref Unsafe.Add(ref bits, n2); int bitsHighLength = bitsLength - n2; // ... compute z_0 = a_0 * a_0 (squaring again!) - Square(valueLow, valueLowLength, - bitsLow, bitsLowLength); + Square(ref valueLow, valueLowLength, + ref bitsLow, bitsLowLength); // ... compute z_2 = a_1 * a_1 (squaring again!) - Square(valueHigh, valueHighLength, - bitsHigh, bitsHighLength); + Square(ref valueHigh, valueHighLength, + ref bitsHigh, bitsHighLength); int foldLength = valueHighLength + 1; int coreLength = foldLength + foldLength; - if (coreLength < AllocationThreshold) - { - uint* fold = stackalloc uint[foldLength]; - new Span(fold, foldLength).Clear(); - uint* core = stackalloc uint[coreLength]; - new Span(core, coreLength).Clear(); - - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(valueHigh, valueHighLength, - valueLow, valueLowLength, - fold, foldLength); - - // ... compute z_1 = z_a * z_a - z_0 - z_2 - Square(fold, foldLength, - core, coreLength); - SubtractCore(bitsHigh, bitsHighLength, - bitsLow, bitsLowLength, - core, coreLength); - - // ... and finally merge the result! :-) - AddSelf(bits + n, bitsLength - n, core, coreLength); - } - else + bool stackAllocRequired = coreLength < AllocationThreshold; + Span fold = stackAllocRequired ? stackalloc uint[foldLength] : new uint[foldLength]; + Span core = stackAllocRequired ? stackalloc uint[coreLength] : new uint[coreLength]; + + if (stackAllocRequired) { - fixed (uint* fold = new uint[foldLength], - core = new uint[coreLength]) - { - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(valueHigh, valueHighLength, - valueLow, valueLowLength, - fold, foldLength); - - // ... compute z_1 = z_a * z_a - z_0 - z_2 - Square(fold, foldLength, - core, coreLength); - SubtractCore(bitsHigh, bitsHighLength, - bitsLow, bitsLowLength, - core, coreLength); - - // ... and finally merge the result! :-) - AddSelf(bits + n, bitsLength - n, core, coreLength); - } + fold.Clear(); + core.Clear(); } + + // ... compute z_a = a_1 + a_0 (call it fold...) + Add(ref valueHigh, valueHighLength, + ref valueLow, valueLowLength, + ref GetReference(fold), foldLength); + + // ... compute z_1 = z_a * z_a - z_0 - z_2 + Square(ref GetReference(fold), foldLength, + ref GetReference(core), coreLength); + SubtractCore(ref bitsHigh, bitsHighLength, + ref bitsLow, bitsLowLength, + ref GetReference(core), coreLength); + + // ... and finally merge the result! :-) + AddSelf(ref Unsafe.Add(ref bits, n), bitsLength - n, ref GetReference(core), coreLength); } } @@ -170,22 +152,30 @@ public static uint[] Multiply(uint[] left, uint right) // a_i * b + c <= 2^32 - 1 + (2^32 - 1)^2 < 2^64 - 1, // we are safe regarding to overflows. + uint[] bits = new uint[left.Length + 1]; + + Multiply(ref GetArrayDataReference(left), left.Length, right, ref GetArrayDataReference(bits)); + + return bits; + } + + private static void Multiply(ref uint left, int leftLength, + uint right, + ref uint bits) + { int i = 0; ulong carry = 0UL; - uint[] bits = new uint[left.Length + 1]; - for (; i < left.Length; i++) + for ( ; i < leftLength; i++) { - ulong digits = (ulong)left[i] * right + carry; - bits[i] = unchecked((uint)digits); + ulong digits = (ulong)Unsafe.Add(ref left, i) * right + carry; + Unsafe.Add(ref bits, i) = unchecked((uint)digits); carry = digits >> 32; } - bits[i] = (uint)carry; - - return bits; + Unsafe.Add(ref bits, i) = (uint)carry; } - public static unsafe uint[] Multiply(uint[] left, uint[] right) + public static uint[] Multiply(uint[] left, uint[] right) { Debug.Assert(left != null); Debug.Assert(right != null); @@ -196,12 +186,9 @@ public static unsafe uint[] Multiply(uint[] left, uint[] right) uint[] bits = new uint[left.Length + right.Length]; - fixed (uint* l = left, r = right, b = bits) - { - Multiply(l, left.Length, - r, right.Length, - b, bits.Length); - } + Multiply(ref GetArrayDataReference(left), left.Length, + ref GetArrayDataReference(right), right.Length, + ref GetArrayDataReference(bits), bits.Length); return bits; } @@ -209,9 +196,9 @@ public static unsafe uint[] Multiply(uint[] left, uint[] right) // Mutable for unit testing... private static int MultiplyThreshold = 32; - private static unsafe void Multiply(uint* left, int leftLength, - uint* right, int rightLength, - uint* bits, int bitsLength) + private static void Multiply(ref uint left, int leftLength, + ref uint right, int rightLength, + ref uint bits, int bitsLength) { Debug.Assert(leftLength >= 0); Debug.Assert(rightLength >= 0); @@ -236,17 +223,19 @@ private static unsafe void Multiply(uint* left, int leftLength, // z_i+j + a_j * b_i + c <= 2(2^32 - 1) + (2^32 - 1)^2 = // = 2^64 - 1 (which perfectly matches with ulong!). + ref uint elementPtr = ref left; for (int i = 0; i < rightLength; i++) { ulong carry = 0UL; for (int j = 0; j < leftLength; j++) { - ulong digits = bits[i + j] + carry - + (ulong)left[j] * right[i]; - bits[i + j] = unchecked((uint)digits); + elementPtr = ref Unsafe.Add(ref bits, i + j); + ulong digits = elementPtr + carry + + (ulong)Unsafe.Add(ref left, j) * Unsafe.Add(ref right, i); + elementPtr = unchecked((uint)digits); carry = digits >> 32; } - bits[i + leftLength] = (uint)carry; + Unsafe.Add(ref bits, i + leftLength) = (uint)carry; } } else @@ -267,101 +256,75 @@ private static unsafe void Multiply(uint* left, int leftLength, int n2 = n << 1; // ... split left like a = (a_1 << n) + a_0 - uint* leftLow = left; + ref uint leftLow = ref left; int leftLowLength = n; - uint* leftHigh = left + n; + ref uint leftHigh = ref Unsafe.Add(ref left, n); int leftHighLength = leftLength - n; // ... split right like b = (b_1 << n) + b_0 - uint* rightLow = right; + ref uint rightLow = ref right; int rightLowLength = n; - uint* rightHigh = right + n; + ref uint rightHigh = ref Unsafe.Add(ref right, n); int rightHighLength = rightLength - n; // ... prepare our result array (to reuse its memory) - uint* bitsLow = bits; + ref uint bitsLow = ref bits; int bitsLowLength = n2; - uint* bitsHigh = bits + n2; + ref uint bitsHigh = ref Unsafe.Add(ref bits, n2); int bitsHighLength = bitsLength - n2; // ... compute z_0 = a_0 * b_0 (multiply again) - Multiply(leftLow, leftLowLength, - rightLow, rightLowLength, - bitsLow, bitsLowLength); + Multiply(ref leftLow, leftLowLength, + ref rightLow, rightLowLength, + ref bitsLow, bitsLowLength); // ... compute z_2 = a_1 * b_1 (multiply again) - Multiply(leftHigh, leftHighLength, - rightHigh, rightHighLength, - bitsHigh, bitsHighLength); + Multiply(ref leftHigh, leftHighLength, + ref rightHigh, rightHighLength, + ref bitsHigh, bitsHighLength); int leftFoldLength = leftHighLength + 1; int rightFoldLength = rightHighLength + 1; int coreLength = leftFoldLength + rightFoldLength; - if (coreLength < AllocationThreshold) + bool stackAllocRequired = coreLength < AllocationThreshold; + Span leftFold = stackAllocRequired ? stackalloc uint[leftFoldLength] : new uint[leftFoldLength]; + Span rightFold = stackAllocRequired ? stackalloc uint[rightFoldLength] : new uint[rightFoldLength]; + Span core = stackAllocRequired ? stackalloc uint[coreLength] : new uint[coreLength]; + + if (stackAllocRequired) { - uint* leftFold = stackalloc uint[leftFoldLength]; - new Span(leftFold, leftFoldLength).Clear(); - uint* rightFold = stackalloc uint[rightFoldLength]; - new Span(rightFold, rightFoldLength).Clear(); - uint* core = stackalloc uint[coreLength]; - new Span(core, coreLength).Clear(); - - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(leftHigh, leftHighLength, - leftLow, leftLowLength, - leftFold, leftFoldLength); - - // ... compute z_b = b_1 + b_0 (call it fold...) - Add(rightHigh, rightHighLength, - rightLow, rightLowLength, - rightFold, rightFoldLength); - - // ... compute z_1 = z_a * z_b - z_0 - z_2 - Multiply(leftFold, leftFoldLength, - rightFold, rightFoldLength, - core, coreLength); - SubtractCore(bitsHigh, bitsHighLength, - bitsLow, bitsLowLength, - core, coreLength); - - // ... and finally merge the result! :-) - AddSelf(bits + n, bitsLength - n, core, coreLength); - } - else - { - fixed (uint* leftFold = new uint[leftFoldLength], - rightFold = new uint[rightFoldLength], - core = new uint[coreLength]) - { - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(leftHigh, leftHighLength, - leftLow, leftLowLength, - leftFold, leftFoldLength); - - // ... compute z_b = b_1 + b_0 (call it fold...) - Add(rightHigh, rightHighLength, - rightLow, rightLowLength, - rightFold, rightFoldLength); - - // ... compute z_1 = z_a * z_b - z_0 - z_2 - Multiply(leftFold, leftFoldLength, - rightFold, rightFoldLength, - core, coreLength); - SubtractCore(bitsHigh, bitsHighLength, - bitsLow, bitsLowLength, - core, coreLength); - - // ... and finally merge the result! :-) - AddSelf(bits + n, bitsLength - n, core, coreLength); - } + leftFold.Clear(); + rightFold.Clear(); + core.Clear(); } + + // ... compute z_a = a_1 + a_0 (call it fold...) + Add(ref leftHigh, leftHighLength, + ref leftLow, leftLowLength, + ref GetReference(leftFold), leftFoldLength); + + // ... compute z_b = b_1 + b_0 (call it fold...) + Add(ref rightHigh, rightHighLength, + ref rightLow, rightLowLength, + ref GetReference(rightFold), rightFoldLength); + + // ... compute z_1 = z_a * z_b - z_0 - z_2 + Multiply(ref GetReference(leftFold), leftFoldLength, + ref GetReference(rightFold), rightFoldLength, + ref GetReference(core), coreLength); + SubtractCore(ref bitsHigh, bitsHighLength, + ref bitsLow, bitsLowLength, + ref GetReference(core), coreLength); + + // ... and finally merge the result! :-) + AddSelf(ref Unsafe.Add(ref bits, n), bitsLength - n, ref GetReference(core), coreLength); } } - private static unsafe void SubtractCore(uint* left, int leftLength, - uint* right, int rightLength, - uint* core, int coreLength) + private static void SubtractCore(ref uint left, int leftLength, + ref uint right, int rightLength, + ref uint core, int coreLength) { Debug.Assert(leftLength >= 0); Debug.Assert(rightLength >= 0); @@ -379,22 +342,26 @@ private static unsafe void SubtractCore(uint* left, int leftLength, int i = 0; long carry = 0L; + ref uint elementPtr = ref left; for (; i < rightLength; i++) { - long digit = (core[i] + carry) - left[i] - right[i]; - core[i] = unchecked((uint)digit); + elementPtr = ref Unsafe.Add(ref core, i); + long digit = (elementPtr + carry) - Unsafe.Add(ref left, i) - Unsafe.Add(ref right, i); + elementPtr = unchecked((uint)digit); carry = digit >> 32; } for (; i < leftLength; i++) { - long digit = (core[i] + carry) - left[i]; - core[i] = unchecked((uint)digit); + elementPtr = ref Unsafe.Add(ref core, i); + long digit = (elementPtr + carry) - Unsafe.Add(ref left, i); + elementPtr = unchecked((uint)digit); carry = digit >> 32; } for (; carry != 0 && i < coreLength; i++) { - long digit = core[i] + carry; - core[i] = (uint)digit; + elementPtr = ref Unsafe.Add(ref core, i); + long digit = elementPtr + carry; + elementPtr = (uint)digit; carry = digit >> 32; } } From 27cd3b662749dc0a68061e138e849a1b1e53665c Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 28 Apr 2020 11:33:31 +0300 Subject: [PATCH 02/88] Duplicate code replaced with static local --- .../Numerics/BigIntegerCalculator.SquMul.cs | 185 ++++++++++-------- 1 file changed, 104 insertions(+), 81 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index a44bbb9880848..1c478dcf80506 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -94,52 +94,62 @@ private static unsafe void Square(ref uint value, int valueLength, int n2 = n << 1; // ... split value like a = (a_1 << n) + a_0 - ref uint valueLow = ref value; - int valueLowLength = n; - ref uint valueHigh = ref Unsafe.Add(ref value, n); - int valueHighLength = valueLength - n; + Span valueLow = CreateSpan(ref value, n); + Span valueHigh = CreateSpan(ref Unsafe.Add(ref value, n), valueLength - n); // ... prepare our result array (to reuse its memory) - ref uint bitsLow = ref bits; - int bitsLowLength = n2; - ref uint bitsHigh = ref Unsafe.Add(ref bits, n2); - int bitsHighLength = bitsLength - n2; + Span bitsLow = CreateSpan(ref bits, n2); + Span bitsHigh = CreateSpan(ref Unsafe.Add(ref bits, n2), bitsLength - n2); // ... compute z_0 = a_0 * a_0 (squaring again!) - Square(ref valueLow, valueLowLength, - ref bitsLow, bitsLowLength); + Square(ref GetReference(valueLow), valueLow.Length, + ref GetReference(bitsLow), bitsLow.Length); // ... compute z_2 = a_1 * a_1 (squaring again!) - Square(ref valueHigh, valueHighLength, - ref bitsHigh, bitsHighLength); + Square(ref GetReference(valueHigh), valueHigh.Length, + ref GetReference(bitsHigh), bitsHigh.Length); - int foldLength = valueHighLength + 1; + int foldLength = valueHigh.Length + 1; int coreLength = foldLength + foldLength; - bool stackAllocRequired = coreLength < AllocationThreshold; - Span fold = stackAllocRequired ? stackalloc uint[foldLength] : new uint[foldLength]; - Span core = stackAllocRequired ? stackalloc uint[coreLength] : new uint[coreLength]; + Span result = CreateSpan(ref Unsafe.Add(ref bits, n), bitsLength - n); - if (stackAllocRequired) + if (coreLength < AllocationThreshold) { - fold.Clear(); - core.Clear(); + SquareFinal(valueHigh, valueLow, + ZeroMem(stackalloc uint[foldLength]), ZeroMem(stackalloc uint[coreLength]), + bitsHigh, bitsLow, + result); + } + else + { + SquareFinal(valueHigh, valueLow, + new uint[foldLength], new uint[coreLength], + bitsHigh, bitsLow, + result); } - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(ref valueHigh, valueHighLength, - ref valueLow, valueLowLength, - ref GetReference(fold), foldLength); - - // ... compute z_1 = z_a * z_a - z_0 - z_2 - Square(ref GetReference(fold), foldLength, - ref GetReference(core), coreLength); - SubtractCore(ref bitsHigh, bitsHighLength, - ref bitsLow, bitsLowLength, - ref GetReference(core), coreLength); - - // ... and finally merge the result! :-) - AddSelf(ref Unsafe.Add(ref bits, n), bitsLength - n, ref GetReference(core), coreLength); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static void SquareFinal(Span valueHigh, Span valueLow, + Span fold, Span core, + Span bitsHigh, Span bitsLow, + Span result) + { + // ... compute z_a = a_1 + a_0 (call it fold...) + Add(ref GetReference(valueHigh), valueHigh.Length, + ref GetReference(valueLow), valueLow.Length, + ref GetReference(fold), fold.Length); + + // ... compute z_1 = z_a * z_a - z_0 - z_2 + Square(ref GetReference(fold), fold.Length, + ref GetReference(core), core.Length); + SubtractCore(ref GetReference(bitsHigh), bitsHigh.Length, + ref GetReference(bitsLow), bitsLow.Length, + ref GetReference(core), core.Length); + + // ... and finally merge the result! :-) + AddSelf(ref GetReference(result), result.Length, ref GetReference(core), core.Length); + } } } @@ -196,6 +206,13 @@ ref GetArrayDataReference(right), right.Length, // Mutable for unit testing... private static int MultiplyThreshold = 32; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Span ZeroMem(Span memory) + { + memory.Clear(); + return memory; + } + private static void Multiply(ref uint left, int leftLength, ref uint right, int rightLength, ref uint bits, int bitsLength) @@ -256,69 +273,75 @@ private static void Multiply(ref uint left, int leftLength, int n2 = n << 1; // ... split left like a = (a_1 << n) + a_0 - ref uint leftLow = ref left; - int leftLowLength = n; - ref uint leftHigh = ref Unsafe.Add(ref left, n); - int leftHighLength = leftLength - n; + Span leftLow = CreateSpan(ref left, n); + Span leftHigh = CreateSpan(ref Unsafe.Add(ref left, n), leftLength - n); // ... split right like b = (b_1 << n) + b_0 - ref uint rightLow = ref right; - int rightLowLength = n; - ref uint rightHigh = ref Unsafe.Add(ref right, n); - int rightHighLength = rightLength - n; + Span rightLow = CreateSpan(ref right, n); + Span rightHigh = CreateSpan(ref Unsafe.Add(ref right, n), rightLength - n); // ... prepare our result array (to reuse its memory) - ref uint bitsLow = ref bits; - int bitsLowLength = n2; - ref uint bitsHigh = ref Unsafe.Add(ref bits, n2); - int bitsHighLength = bitsLength - n2; + Span bitsLow = CreateSpan(ref bits, n2); + Span bitsHigh = CreateSpan(ref Unsafe.Add(ref bits, n2), bitsLength - n2); // ... compute z_0 = a_0 * b_0 (multiply again) - Multiply(ref leftLow, leftLowLength, - ref rightLow, rightLowLength, - ref bitsLow, bitsLowLength); + Multiply(ref GetReference(leftLow), leftLow.Length, + ref GetReference(rightLow), rightLow.Length, + ref GetReference(bitsLow), bitsLow.Length); // ... compute z_2 = a_1 * b_1 (multiply again) - Multiply(ref leftHigh, leftHighLength, - ref rightHigh, rightHighLength, - ref bitsHigh, bitsHighLength); + Multiply(ref GetReference(leftHigh), leftHigh.Length, + ref GetReference(rightHigh), rightHigh.Length, + ref GetReference(bitsHigh), bitsHigh.Length); - int leftFoldLength = leftHighLength + 1; - int rightFoldLength = rightHighLength + 1; + int leftFoldLength = leftHigh.Length + 1; + int rightFoldLength = rightHigh.Length + 1; int coreLength = leftFoldLength + rightFoldLength; - bool stackAllocRequired = coreLength < AllocationThreshold; - Span leftFold = stackAllocRequired ? stackalloc uint[leftFoldLength] : new uint[leftFoldLength]; - Span rightFold = stackAllocRequired ? stackalloc uint[rightFoldLength] : new uint[rightFoldLength]; - Span core = stackAllocRequired ? stackalloc uint[coreLength] : new uint[coreLength]; + Span result = CreateSpan(ref Unsafe.Add(ref bits, n), bitsLength - n); - if (stackAllocRequired) + if (coreLength < AllocationThreshold) + { + MultiplyFinal(leftHigh, leftLow, ZeroMem(stackalloc uint[leftFoldLength]), + rightHigh, rightLow, ZeroMem(stackalloc uint[rightFoldLength]), + bitsHigh, bitsLow, ZeroMem(stackalloc uint[coreLength]), + result); + } + else { - leftFold.Clear(); - rightFold.Clear(); - core.Clear(); + MultiplyFinal(leftHigh, leftLow, new uint[leftFoldLength], + rightHigh, rightLow, new uint[rightFoldLength], + bitsHigh, bitsLow, new uint[coreLength], + result); } - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(ref leftHigh, leftHighLength, - ref leftLow, leftLowLength, - ref GetReference(leftFold), leftFoldLength); - - // ... compute z_b = b_1 + b_0 (call it fold...) - Add(ref rightHigh, rightHighLength, - ref rightLow, rightLowLength, - ref GetReference(rightFold), rightFoldLength); - - // ... compute z_1 = z_a * z_b - z_0 - z_2 - Multiply(ref GetReference(leftFold), leftFoldLength, - ref GetReference(rightFold), rightFoldLength, - ref GetReference(core), coreLength); - SubtractCore(ref bitsHigh, bitsHighLength, - ref bitsLow, bitsLowLength, - ref GetReference(core), coreLength); - - // ... and finally merge the result! :-) - AddSelf(ref Unsafe.Add(ref bits, n), bitsLength - n, ref GetReference(core), coreLength); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static void MultiplyFinal(Span leftHigh, Span leftLow, Span leftFold, + Span rightHigh, Span rightLow, Span rightFold, + Span bitsHigh, Span bitsLow, Span core, + Span result) + { + // ... compute z_a = a_1 + a_0 (call it fold...) + Add(ref GetReference(leftHigh), leftHigh.Length, + ref GetReference(leftLow), leftLow.Length, + ref GetReference(leftFold), leftFold.Length); + + // ... compute z_b = b_1 + b_0 (call it fold...) + Add(ref GetReference(rightHigh), rightHigh.Length, + ref GetReference(rightLow), rightLow.Length, + ref GetReference(rightFold), rightFold.Length); + + // ... compute z_1 = z_a * z_b - z_0 - z_2 + Multiply(ref GetReference(leftFold), leftFold.Length, + ref GetReference(rightFold), rightFold.Length, + ref GetReference(core), core.Length); + SubtractCore(ref GetReference(bitsHigh), bitsHigh.Length, + ref GetReference(bitsLow), bitsLow.Length, + ref GetReference(core), core.Length); + + // ... and finally merge the result! :-) + AddSelf(ref GetReference(result), result.Length, ref GetReference(core), core.Length); + } } } From f6d1f89db0f87c1c44ecc5da1d6501c7658b2c90 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 28 Apr 2020 12:37:29 +0300 Subject: [PATCH 03/88] Spannified internals --- .../src/System.Runtime.Numerics.csproj | 1 + .../Numerics/BigIntegerCalculator.AddSub.cs | 132 ++++++------- .../BigIntegerCalculator.BitsBuffer.cs | 16 +- .../BigIntegerCalculator.FastReducer.cs | 16 +- .../Numerics/BigIntegerCalculator.SquMul.cs | 177 +++++++----------- .../Numerics/BigIntegerCalculator.Utils.cs | 16 ++ 6 files changed, 163 insertions(+), 195 deletions(-) create mode 100644 src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs diff --git a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj index 779b89add1e16..fd40c4a5fd0ea 100644 --- a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj +++ b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj @@ -14,6 +14,7 @@ + diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index 83085f1b86b43..8c78d1dc50884 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -21,28 +21,26 @@ public static uint[] Add(uint[] left, uint right) uint[] bits = new uint[left.Length + 1]; - Add(ref GetArrayDataReference(left), left.Length, - right, - ref GetArrayDataReference(bits)); + Add(left, right, ref GetArrayDataReference(bits)); return bits; } - private static void Add(ref uint left, int leftLength, + private static void Add(ReadOnlySpan left, uint right, ref uint bits) { - long digit = (long)left + right; + long digit = (long)GetReference(left) + right; bits = unchecked((uint)digit); long carry = digit >> 32; - for (int i = 1; i < leftLength; i++) + for (int i = 1; i < left.Length; i++) { - digit = Unsafe.Add(ref left, i) + carry; + digit = Unsafe.Add(ref GetReference(left), i) + carry; Unsafe.Add(ref bits, i) = unchecked((uint)digit); carry = digit >> 32; } - Unsafe.Add(ref bits, leftLength) = (uint)carry; + Unsafe.Add(ref bits, left.Length) = (uint)carry; } public static uint[] Add(uint[] left, uint[] right) @@ -56,21 +54,17 @@ public static uint[] Add(uint[] left, uint[] right) uint[] bits = new uint[left.Length + 1]; - Add(ref GetArrayDataReference(left), left.Length, - ref GetArrayDataReference(right), right.Length, - ref GetArrayDataReference(bits), bits.Length); + Add(left, right, bits); return bits; } - private static void Add(ref uint left, int leftLength, - ref uint right, int rightLength, - ref uint bits, int bitsLength) + private static void Add(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(bitsLength == leftLength + 1); + Debug.Assert(left.Length >= 0); + Debug.Assert(right.Length >= 0); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(bits.Length == left.Length + 1); // Executes the "grammar-school" algorithm for computing z = a + b. // While calculating z_i = a_i + b_i we take care of overflow: @@ -80,27 +74,26 @@ private static void Add(ref uint left, int leftLength, int i = 0; long carry = 0L; - for (; i < rightLength; i++) + for (; i < right.Length; i++) { - long digit = (Unsafe.Add(ref left, i) + carry) + Unsafe.Add(ref right, i); - Unsafe.Add(ref bits, i) = unchecked((uint)digit); + long digit = (Unsafe.Add(ref GetReference(left), i) + carry) + Unsafe.Add(ref GetReference(right), i); + Unsafe.Add(ref GetReference(bits), i) = unchecked((uint)digit); carry = digit >> 32; } - for (; i < leftLength; i++) + for (; i < left.Length; i++) { - long digit = Unsafe.Add(ref left, i) + carry; - Unsafe.Add(ref bits, i) = unchecked((uint)digit); + long digit = Unsafe.Add(ref GetReference(left), i) + carry; + Unsafe.Add(ref GetReference(bits), i) = unchecked((uint)digit); carry = digit >> 32; } - Unsafe.Add(ref bits, i) = (uint)carry; + Unsafe.Add(ref GetReference(bits), i) = (uint)carry; } - private static void AddSelf(ref uint left, int leftLength, - ref uint right, int rightLength) + private static void AddSelf(Span left, ReadOnlySpan right) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); + Debug.Assert(left.Length >= 0); + Debug.Assert(right.Length >= 0); + Debug.Assert(left.Length >= right.Length); // Executes the "grammar-school" algorithm for computing z = a + b. // Same as above, but we're writing the result directly to a and @@ -108,17 +101,17 @@ private static void AddSelf(ref uint left, int leftLength, int i = 0; long carry = 0L; - ref uint leftElement = ref left; - for (; i < rightLength; i++) + ref uint leftElement = ref NullRef; + for (; i < right.Length; i++) { - leftElement = ref Unsafe.Add(ref left, i); - long digit = (leftElement + carry) + Unsafe.Add(ref right, i); + leftElement = ref Unsafe.Add(ref GetReference(left), i); + long digit = (leftElement + carry) + Unsafe.Add(ref GetReference(right), i); leftElement = unchecked((uint)digit); carry = digit >> 32; } - for (; carry != 0 && i < leftLength; i++) + for (; carry != 0 && i < left.Length; i++) { - leftElement = ref Unsafe.Add(ref left, i); + leftElement = ref Unsafe.Add(ref GetReference(left), i); long digit = leftElement + carry; leftElement = (uint)digit; carry = digit >> 32; @@ -139,24 +132,20 @@ public static uint[] Subtract(uint[] left, uint right) uint[] bits = new uint[left.Length]; - Subtract(ref GetArrayDataReference(left), left.Length, - right, - ref GetArrayDataReference(bits)); + Subtract(left, right, ref GetArrayDataReference(bits)); return bits; } - private static void Subtract(ref uint left, int leftLength, - uint right, - ref uint bits) + private static void Subtract(ReadOnlySpan left, uint right, ref uint bits) { - long digit = (long)left - right; + long digit = (long)GetReference(left) - right; bits = unchecked((uint)digit); long carry = digit >> 32; - for (int i = 1; i < leftLength; i++) + for (int i = 1; i < left.Length; i++) { - digit = Unsafe.Add(ref left, i) + carry; + digit = Unsafe.Add(ref GetReference(left), i) + carry; Unsafe.Add(ref bits, i) = unchecked((uint)digit); carry = digit >> 32; } @@ -174,22 +163,18 @@ public static uint[] Subtract(uint[] left, uint[] right) uint[] bits = new uint[left.Length]; - Subtract(ref GetArrayDataReference(left), left.Length, - ref GetArrayDataReference(right), right.Length, - ref GetArrayDataReference(bits), bits.Length); + Subtract(left, right, bits); return bits; } - private static void Subtract(ref uint left, int leftLength, - ref uint right, int rightLength, - ref uint bits, int bitsLength) + private static void Subtract(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(Compare(ref left, leftLength, ref right, rightLength) >= 0); - Debug.Assert(bitsLength == leftLength); + Debug.Assert(left.Length >= 0); + Debug.Assert(right.Length >= 0); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(Compare(ref GetReference(left), left.Length, ref GetReference(right), right.Length) >= 0); + Debug.Assert(bits.Length == left.Length); // Executes the "grammar-school" algorithm for computing z = a - b. // While calculating z_i = a_i - b_i we take care of overflow: @@ -199,29 +184,28 @@ private static void Subtract(ref uint left, int leftLength, int i = 0; long carry = 0L; - for (; i < rightLength; i++) + for (; i < right.Length; i++) { - long digit = (Unsafe.Add(ref left, i) + carry) - Unsafe.Add(ref right, i); - Unsafe.Add(ref bits, i) = unchecked((uint)digit); + long digit = (Unsafe.Add(ref GetReference(left), i) + carry) - Unsafe.Add(ref GetReference(right), i); + Unsafe.Add(ref GetReference(bits), i) = unchecked((uint)digit); carry = digit >> 32; } - for (; i < leftLength; i++) + for (; i < left.Length; i++) { - long digit = Unsafe.Add(ref left, i) + carry; - Unsafe.Add(ref bits, i) = (uint)digit; + long digit = Unsafe.Add(ref GetReference(left), i) + carry; + Unsafe.Add(ref GetReference(bits), i) = (uint)digit; carry = digit >> 32; } Debug.Assert(carry == 0); } - private static void SubtractSelf(ref uint left, int leftLength, - ref uint right, int rightLength) + private static void SubtractSelf(Span left, ReadOnlySpan right) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(Compare(ref left, leftLength, ref right, rightLength) >= 0); + Debug.Assert(left.Length >= 0); + Debug.Assert(right.Length >= 0); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(Compare(ref GetReference(left), left.Length, ref GetReference(right), right.Length) >= 0); // Executes the "grammar-school" algorithm for computing z = a - b. // Same as above, but we're writing the result directly to a and @@ -229,17 +213,17 @@ private static void SubtractSelf(ref uint left, int leftLength, int i = 0; long carry = 0L; - ref uint leftElement = ref left; - for (; i < rightLength; i++) + ref uint leftElement = ref NullRef; + for (; i < right.Length; i++) { - leftElement = ref Unsafe.Add(ref left, i); - long digit = (leftElement + carry) - Unsafe.Add(ref right, i); + leftElement = ref Unsafe.Add(ref GetReference(left), i); + long digit = (leftElement + carry) - Unsafe.Add(ref GetReference(right), i); leftElement = unchecked((uint)digit); carry = digit >> 32; } - for (; carry != 0 && i < leftLength; i++) + for (; carry != 0 && i < left.Length; i++) { - leftElement = ref Unsafe.Add(ref left, i); + leftElement = ref Unsafe.Add(ref GetReference(left), i); long digit = leftElement + carry; leftElement = (uint)digit; carry = digit >> 32; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index 489de32e7c796..25b7b178f78a5 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -60,15 +60,15 @@ public void MultiplySelf(ref BitsBuffer value, if (_length < value._length) { - Multiply(ref v, value._length, - ref b, _length, - ref t, _length + value._length); + Multiply(CreateSpan(ref v, value._length), + CreateSpan(ref b, _length), + CreateSpan(ref t, _length + value._length)); } else { - Multiply(ref b, _length, - ref v, value._length, - ref t, _length + value._length); + Multiply(CreateSpan(ref b, _length), + CreateSpan(ref v, value._length), + CreateSpan(ref t, _length + value._length)); } Apply(ref temp, _length + value._length); @@ -85,8 +85,8 @@ public void SquareSelf(ref BitsBuffer temp) ref uint b = ref GetArrayDataReference(_bits); ref uint t = ref GetArrayDataReference(temp._bits); - Square(ref b, _length, - ref t, _length + _length); + Square(CreateSpan(ref b, _length), + CreateSpan(ref t, _length + _length)); Apply(ref temp, _length + _length); } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 3af59210b14f7..2e7da1cfd2ff4 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -95,15 +95,15 @@ private static int DivMul(uint[] left, int leftLength, if (leftLength < rightLength) { - Multiply(ref r, rightLength, - ref Unsafe.Add(ref l, k), leftLength, - ref b, leftLength + rightLength); + Multiply(CreateSpan(ref r, rightLength), + CreateSpan(ref Unsafe.Add(ref l, k), leftLength), + CreateSpan(ref b, leftLength + rightLength)); } else { - Multiply(ref Unsafe.Add(ref l, k), leftLength, - ref r, rightLength, - ref b, leftLength + rightLength); + Multiply(CreateSpan(ref Unsafe.Add(ref l, k), leftLength), + CreateSpan(ref r, rightLength), + CreateSpan(ref b, leftLength + rightLength)); } return ActualLength(bits, leftLength + rightLength); @@ -135,12 +135,12 @@ private static int SubMod(uint[] left, int leftLength, ref uint r = ref GetArrayDataReference(right); ref uint m = ref GetArrayDataReference(modulus); - SubtractSelf(ref l, leftLength, ref r, rightLength); + SubtractSelf(CreateSpan(ref l, leftLength), CreateSpan(ref r, rightLength)); leftLength = ActualLength(left, leftLength); while (Compare(ref l, leftLength, ref m, modulus.Length) >= 0) { - SubtractSelf(ref l, leftLength, ref m, modulus.Length); + SubtractSelf(CreateSpan(ref l, leftLength), CreateSpan(ref m, modulus.Length)); leftLength = ActualLength(left, leftLength); } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 1c478dcf80506..38ab2e7db7e27 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -19,8 +19,7 @@ public static uint[] Square(uint[] value) uint[] bits = new uint[value.Length + value.Length]; - Square(ref GetArrayDataReference(value), value.Length, - ref GetArrayDataReference(bits), bits.Length); + Square(value, bits); return bits; } @@ -29,11 +28,10 @@ public static uint[] Square(uint[] value) private static int SquareThreshold = 32; private static int AllocationThreshold = 256; - private static unsafe void Square(ref uint value, int valueLength, - ref uint bits, int bitsLength) + private static void Square(ReadOnlySpan value, Span bits) { - Debug.Assert(valueLength >= 0); - Debug.Assert(bitsLength == valueLength + valueLength); + Debug.Assert(value.Length >= 0); + Debug.Assert(bits.Length == value.Length + value.Length); // Executes different algorithms for computing z = a * a // based on the actual length of a. If a is "small" enough @@ -44,7 +42,7 @@ private static unsafe void Square(ref uint value, int valueLength, // NOTE: useful thresholds needs some "empirical" testing, // which are smaller in DEBUG mode for testing purpose. - if (valueLength < SquareThreshold) + if (value.Length < SquareThreshold) { // Squares the bits using the "grammar-school" method. // Envisioning the "rhombus" of a pen-and-paper calculation @@ -57,21 +55,21 @@ private static unsafe void Square(ref uint value, int valueLength, // = 2^64 - 1 (which perfectly matches with ulong!). But // here we would need an UInt65... Hence, we split these // operation and do some extra shifts. - ref uint elementPtr = ref value; - for (int i = 0; i < valueLength; i++) + ref uint elementPtr = ref NullRef; + for (int i = 0; i < value.Length; i++) { ulong carry = 0UL; for (int j = 0; j < i; j++) { - elementPtr = ref Unsafe.Add(ref bits, i + j); + elementPtr = ref Unsafe.Add(ref GetReference(bits), i + j); ulong digit1 = elementPtr + carry; - ulong digit2 = (ulong)Unsafe.Add(ref value, j) * Unsafe.Add(ref value, i); + ulong digit2 = (ulong)Unsafe.Add(ref GetReference(value), j) * Unsafe.Add(ref GetReference(value), i); elementPtr = unchecked((uint)(digit1 + (digit2 << 1))); carry = (digit2 + (digit1 >> 1)) >> 31; } - elementPtr = ref Unsafe.Add(ref value, i); + elementPtr = ref Unsafe.Add(ref GetReference(value), i); ulong digits = (ulong)elementPtr * elementPtr + carry; - elementPtr = ref Unsafe.Add(ref bits, i + i); + elementPtr = ref Unsafe.Add(ref GetReference(bits), i + i); elementPtr = unchecked((uint)digits); Unsafe.Add(ref elementPtr, 1) = (uint)(digits >> 32); } @@ -90,29 +88,27 @@ private static unsafe void Square(ref uint value, int valueLength, // Say we want to compute z = a * a ... // ... we need to determine our new length (just the half) - int n = valueLength >> 1; + int n = value.Length >> 1; int n2 = n << 1; // ... split value like a = (a_1 << n) + a_0 - Span valueLow = CreateSpan(ref value, n); - Span valueHigh = CreateSpan(ref Unsafe.Add(ref value, n), valueLength - n); + ReadOnlySpan valueLow = value.Slice(0, n); + ReadOnlySpan valueHigh = value.Slice(n); // ... prepare our result array (to reuse its memory) - Span bitsLow = CreateSpan(ref bits, n2); - Span bitsHigh = CreateSpan(ref Unsafe.Add(ref bits, n2), bitsLength - n2); + Span bitsLow = bits.Slice(0, n2); + Span bitsHigh = bits.Slice(n2); // ... compute z_0 = a_0 * a_0 (squaring again!) - Square(ref GetReference(valueLow), valueLow.Length, - ref GetReference(bitsLow), bitsLow.Length); + Square(valueLow, bitsLow); // ... compute z_2 = a_1 * a_1 (squaring again!) - Square(ref GetReference(valueHigh), valueHigh.Length, - ref GetReference(bitsHigh), bitsHigh.Length); + Square(valueHigh, bitsHigh); int foldLength = valueHigh.Length + 1; int coreLength = foldLength + foldLength; - Span result = CreateSpan(ref Unsafe.Add(ref bits, n), bitsLength - n); + Span result = bits.Slice(n); if (coreLength < AllocationThreshold) { @@ -130,25 +126,21 @@ private static unsafe void Square(ref uint value, int valueLength, } [MethodImpl(MethodImplOptions.AggressiveInlining)] - static void SquareFinal(Span valueHigh, Span valueLow, + static void SquareFinal(ReadOnlySpan valueHigh, ReadOnlySpan valueLow, Span fold, Span core, - Span bitsHigh, Span bitsLow, + ReadOnlySpan bitsHigh, ReadOnlySpan bitsLow, Span result) { // ... compute z_a = a_1 + a_0 (call it fold...) - Add(ref GetReference(valueHigh), valueHigh.Length, - ref GetReference(valueLow), valueLow.Length, - ref GetReference(fold), fold.Length); + Add(valueHigh, valueLow, fold); // ... compute z_1 = z_a * z_a - z_0 - z_2 - Square(ref GetReference(fold), fold.Length, - ref GetReference(core), core.Length); - SubtractCore(ref GetReference(bitsHigh), bitsHigh.Length, - ref GetReference(bitsLow), bitsLow.Length, - ref GetReference(core), core.Length); + Square(fold, core); + + SubtractCore(bitsHigh, bitsLow, core); // ... and finally merge the result! :-) - AddSelf(ref GetReference(result), result.Length, ref GetReference(core), core.Length); + AddSelf(result, core); } } } @@ -196,9 +188,7 @@ public static uint[] Multiply(uint[] left, uint[] right) uint[] bits = new uint[left.Length + right.Length]; - Multiply(ref GetArrayDataReference(left), left.Length, - ref GetArrayDataReference(right), right.Length, - ref GetArrayDataReference(bits), bits.Length); + Multiply(left, right, bits); return bits; } @@ -206,21 +196,12 @@ ref GetArrayDataReference(right), right.Length, // Mutable for unit testing... private static int MultiplyThreshold = 32; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Span ZeroMem(Span memory) + private static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - memory.Clear(); - return memory; - } - - private static void Multiply(ref uint left, int leftLength, - ref uint right, int rightLength, - ref uint bits, int bitsLength) - { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(bitsLength == leftLength + rightLength); + Debug.Assert(left.Length >= 0); + Debug.Assert(right.Length >= 0); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(bits.Length == left.Length + right.Length); // Executes different algorithms for computing z = a * b // based on the actual length of b. If b is "small" enough @@ -231,7 +212,7 @@ private static void Multiply(ref uint left, int leftLength, // NOTE: useful thresholds needs some "empirical" testing, // which are smaller in DEBUG mode for testing purpose. - if (rightLength < MultiplyThreshold) + if (right.Length < MultiplyThreshold) { // Multiplies the bits using the "grammar-school" method. // Envisioning the "rhombus" of a pen-and-paper calculation @@ -240,19 +221,19 @@ private static void Multiply(ref uint left, int leftLength, // z_i+j + a_j * b_i + c <= 2(2^32 - 1) + (2^32 - 1)^2 = // = 2^64 - 1 (which perfectly matches with ulong!). - ref uint elementPtr = ref left; - for (int i = 0; i < rightLength; i++) + ref uint elementPtr = ref NullRef; + for (int i = 0; i < right.Length; i++) { ulong carry = 0UL; - for (int j = 0; j < leftLength; j++) + for (int j = 0; j < left.Length; j++) { - elementPtr = ref Unsafe.Add(ref bits, i + j); + elementPtr = ref Unsafe.Add(ref GetReference(bits), i + j); ulong digits = elementPtr + carry - + (ulong)Unsafe.Add(ref left, j) * Unsafe.Add(ref right, i); + + (ulong)Unsafe.Add(ref GetReference(left), j) * Unsafe.Add(ref GetReference(right), i); elementPtr = unchecked((uint)digits); carry = digits >> 32; } - Unsafe.Add(ref bits, i + leftLength) = (uint)carry; + Unsafe.Add(ref GetReference(bits), i + left.Length) = (uint)carry; } } else @@ -269,36 +250,32 @@ private static void Multiply(ref uint left, int leftLength, // Say we want to compute z = a * b ... // ... we need to determine our new length (just the half) - int n = rightLength >> 1; + int n = right.Length >> 1; int n2 = n << 1; // ... split left like a = (a_1 << n) + a_0 - Span leftLow = CreateSpan(ref left, n); - Span leftHigh = CreateSpan(ref Unsafe.Add(ref left, n), leftLength - n); + ReadOnlySpan leftLow = left.Slice(0, n); + ReadOnlySpan leftHigh = left.Slice(n); // ... split right like b = (b_1 << n) + b_0 - Span rightLow = CreateSpan(ref right, n); - Span rightHigh = CreateSpan(ref Unsafe.Add(ref right, n), rightLength - n); + ReadOnlySpan rightLow = right.Slice(0, n); + ReadOnlySpan rightHigh = right.Slice(n); // ... prepare our result array (to reuse its memory) - Span bitsLow = CreateSpan(ref bits, n2); - Span bitsHigh = CreateSpan(ref Unsafe.Add(ref bits, n2), bitsLength - n2); + Span bitsLow = bits.Slice(0, n2); + Span bitsHigh = bits.Slice(n2); // ... compute z_0 = a_0 * b_0 (multiply again) - Multiply(ref GetReference(leftLow), leftLow.Length, - ref GetReference(rightLow), rightLow.Length, - ref GetReference(bitsLow), bitsLow.Length); + Multiply(leftLow, rightLow, bitsLow); // ... compute z_2 = a_1 * b_1 (multiply again) - Multiply(ref GetReference(leftHigh), leftHigh.Length, - ref GetReference(rightHigh), rightHigh.Length, - ref GetReference(bitsHigh), bitsHigh.Length); + Multiply(leftHigh, rightHigh, bitsHigh); int leftFoldLength = leftHigh.Length + 1; int rightFoldLength = rightHigh.Length + 1; int coreLength = leftFoldLength + rightFoldLength; - Span result = CreateSpan(ref Unsafe.Add(ref bits, n), bitsLength - n); + Span result = bits.Slice(n); if (coreLength < AllocationThreshold) { @@ -316,44 +293,34 @@ ref GetReference(rightHigh), rightHigh.Length, } [MethodImpl(MethodImplOptions.AggressiveInlining)] - static void MultiplyFinal(Span leftHigh, Span leftLow, Span leftFold, - Span rightHigh, Span rightLow, Span rightFold, - Span bitsHigh, Span bitsLow, Span core, + static void MultiplyFinal(ReadOnlySpan leftHigh, ReadOnlySpan leftLow, Span leftFold, + ReadOnlySpan rightHigh, ReadOnlySpan rightLow, Span rightFold, + ReadOnlySpan bitsHigh, ReadOnlySpan bitsLow, Span core, Span result) { // ... compute z_a = a_1 + a_0 (call it fold...) - Add(ref GetReference(leftHigh), leftHigh.Length, - ref GetReference(leftLow), leftLow.Length, - ref GetReference(leftFold), leftFold.Length); + Add(leftHigh, leftLow, leftFold); // ... compute z_b = b_1 + b_0 (call it fold...) - Add(ref GetReference(rightHigh), rightHigh.Length, - ref GetReference(rightLow), rightLow.Length, - ref GetReference(rightFold), rightFold.Length); + Add(rightHigh, rightLow, rightFold); // ... compute z_1 = z_a * z_b - z_0 - z_2 - Multiply(ref GetReference(leftFold), leftFold.Length, - ref GetReference(rightFold), rightFold.Length, - ref GetReference(core), core.Length); - SubtractCore(ref GetReference(bitsHigh), bitsHigh.Length, - ref GetReference(bitsLow), bitsLow.Length, - ref GetReference(core), core.Length); + Multiply(leftFold, rightFold, core); + SubtractCore(bitsHigh, bitsLow, core); // ... and finally merge the result! :-) - AddSelf(ref GetReference(result), result.Length, ref GetReference(core), core.Length); + AddSelf(result, core); } } } - private static void SubtractCore(ref uint left, int leftLength, - ref uint right, int rightLength, - ref uint core, int coreLength) + private static void SubtractCore(ReadOnlySpan left, ReadOnlySpan right, Span core) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(coreLength >= 0); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(coreLength >= leftLength); + Debug.Assert(left.Length >= 0); + Debug.Assert(right.Length >= 0); + Debug.Assert(core.Length >= 0); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(core.Length >= left.Length); // Executes a special subtraction algorithm for the multiplication, // which needs to subtract two different values from a core value, @@ -365,24 +332,24 @@ private static void SubtractCore(ref uint left, int leftLength, int i = 0; long carry = 0L; - ref uint elementPtr = ref left; - for (; i < rightLength; i++) + ref uint elementPtr = ref NullRef; + for (; i < right.Length; i++) { - elementPtr = ref Unsafe.Add(ref core, i); - long digit = (elementPtr + carry) - Unsafe.Add(ref left, i) - Unsafe.Add(ref right, i); + elementPtr = ref Unsafe.Add(ref GetReference(core), i); + long digit = (elementPtr + carry) - Unsafe.Add(ref GetReference(left), i) - Unsafe.Add(ref GetReference(right), i); elementPtr = unchecked((uint)digit); carry = digit >> 32; } - for (; i < leftLength; i++) + for (; i < left.Length; i++) { - elementPtr = ref Unsafe.Add(ref core, i); - long digit = (elementPtr + carry) - Unsafe.Add(ref left, i); + elementPtr = ref Unsafe.Add(ref GetReference(core), i); + long digit = (elementPtr + carry) - Unsafe.Add(ref GetReference(left), i); elementPtr = unchecked((uint)digit); carry = digit >> 32; } - for (; carry != 0 && i < coreLength; i++) + for (; carry != 0 && i < core.Length; i++) { - elementPtr = ref Unsafe.Add(ref core, i); + elementPtr = ref Unsafe.Add(ref GetReference(core), i); long digit = elementPtr + carry; elementPtr = (uint)digit; carry = digit >> 32; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs new file mode 100644 index 0000000000000..6072d5e86d5aa --- /dev/null +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -0,0 +1,16 @@ +using System.Runtime.CompilerServices; + +namespace System.Numerics +{ + internal static partial class BigIntegerCalculator + { + private static unsafe ref uint NullRef => ref Unsafe.AsRef(null); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Span ZeroMem(Span memory) + { + memory.Clear(); + return memory; + } + } +} \ No newline at end of file From d8af4fd999eecb28485269a68c45ec4e1d50f8d4 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 28 Apr 2020 12:52:23 +0300 Subject: [PATCH 04/88] Spannified compare --- .../Numerics/BigIntegerCalculator.AddSub.cs | 47 +------------------ .../BigIntegerCalculator.FastReducer.cs | 2 +- .../Numerics/BigIntegerCalculator.Utils.cs | 21 +++++++++ 3 files changed, 24 insertions(+), 46 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index 8c78d1dc50884..d724723024f5b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -173,7 +173,7 @@ private static void Subtract(ReadOnlySpan left, ReadOnlySpan right, Debug.Assert(left.Length >= 0); Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); - Debug.Assert(Compare(ref GetReference(left), left.Length, ref GetReference(right), right.Length) >= 0); + Debug.Assert(Compare(left, right) >= 0); Debug.Assert(bits.Length == left.Length); // Executes the "grammar-school" algorithm for computing z = a - b. @@ -205,7 +205,7 @@ private static void SubtractSelf(Span left, ReadOnlySpan right) Debug.Assert(left.Length >= 0); Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); - Debug.Assert(Compare(ref GetReference(left), left.Length, ref GetReference(right), right.Length) >= 0); + Debug.Assert(Compare(left, right) >= 0); // Executes the "grammar-school" algorithm for computing z = a - b. // Same as above, but we're writing the result directly to a and @@ -231,48 +231,5 @@ private static void SubtractSelf(Span left, ReadOnlySpan right) Debug.Assert(carry == 0); } - - public static int Compare(uint[] left, uint[] right) - { - Debug.Assert(left != null); - Debug.Assert(right != null); - - if (left.Length < right.Length) - return -1; - if (left.Length > right.Length) - return 1; - - for (int i = left.Length - 1; i >= 0; i--) - { - if (left[i] < right[i]) - return -1; - if (left[i] > right[i]) - return 1; - } - - return 0; - } - - private static int Compare(ref uint left, int leftLength, - ref uint right, int rightLength) - { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - - if (leftLength < rightLength) - return -1; - if (leftLength > rightLength) - return 1; - - for (int i = leftLength - 1; i >= 0; i--) - { - if (Unsafe.Add(ref left, i) < Unsafe.Add(ref right, i)) - return -1; - if (Unsafe.Add(ref left, i) > Unsafe.Add(ref right, i)) - return 1; - } - - return 0; - } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 2e7da1cfd2ff4..f806c6668e363 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -138,7 +138,7 @@ private static int SubMod(uint[] left, int leftLength, SubtractSelf(CreateSpan(ref l, leftLength), CreateSpan(ref r, rightLength)); leftLength = ActualLength(left, leftLength); - while (Compare(ref l, leftLength, ref m, modulus.Length) >= 0) + while (Compare(CreateSpan(ref l, leftLength), CreateSpan(ref m, modulus.Length)) >= 0) { SubtractSelf(CreateSpan(ref l, leftLength), CreateSpan(ref m, modulus.Length)); leftLength = ActualLength(left, leftLength); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs index 6072d5e86d5aa..22f2291a3513f 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { @@ -12,5 +13,25 @@ private static Span ZeroMem(Span memory) memory.Clear(); return memory; } + + public static int Compare(ReadOnlySpan left, ReadOnlySpan right) + { + if (left.Length < right.Length) + return -1; + if (left.Length > right.Length) + return 1; + + for (int i = left.Length - 1; i >= 0; i--) + { + uint leftElement = Unsafe.Add(ref GetReference(left), i); + uint rightElement = Unsafe.Add(ref GetReference(right), i); + if (leftElement < rightElement) + return -1; + if (leftElement > rightElement) + return 1; + } + + return 0; + } } } \ No newline at end of file From ca1cf5cea8ee71b4f99917661fc7e50006ca9c0a Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 28 Apr 2020 15:00:18 +0300 Subject: [PATCH 05/88] Spannified div/rem operations --- .../BigIntegerCalculator.BitsBuffer.cs | 12 +- .../Numerics/BigIntegerCalculator.DivRem.cs | 131 +++++++----------- 2 files changed, 56 insertions(+), 87 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index 25b7b178f78a5..74fead0573287 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -99,7 +99,7 @@ public void Reduce(ref FastReducer reducer) _length = reducer.Reduce(_bits, _length); } - public unsafe void Reduce(uint[] modulus) + public void Reduce(uint[] modulus) { Debug.Assert(modulus != null); @@ -108,24 +108,20 @@ public unsafe void Reduce(uint[] modulus) if (_length >= modulus.Length) { - Divide(ref GetArrayDataReference(_bits), _length, - ref GetArrayDataReference(modulus), modulus.Length, - ref Unsafe.AsRef(null), 0); + Divide(new Span(_bits, 0, _length), modulus, default); _length = ActualLength(_bits, modulus.Length); } } - public unsafe void Reduce(ref BitsBuffer modulus) + public void Reduce(ref BitsBuffer modulus) { // Executes a modulo operation using the divide operation. // Thus, no need of any switching here, happens in-line. if (_length >= modulus._length) { - Divide(ref GetArrayDataReference(_bits), _length, - ref GetArrayDataReference(modulus._bits), modulus._length, - ref Unsafe.AsRef(null), 0); + Divide(new Span(_bits, 0, _length), new Span(modulus._bits, 0, modulus._length), default); _length = ActualLength(_bits, modulus._length); } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index dba75021e24a5..07f24e49d2ac0 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -22,22 +22,20 @@ public static uint[] Divide(uint[] left, uint right, uint[] quotient = new uint[left.Length]; - Divide(ref GetArrayDataReference(left), left.Length, - right, - ref GetArrayDataReference(quotient), out remainder); + Divide(left, right, ref GetArrayDataReference(quotient), out remainder); return quotient; } - private static void Divide(ref uint left, int leftLength, + private static void Divide(ReadOnlySpan left, uint right, ref uint quotient, out uint remainder) { ulong carry = 0UL; - for (int i = leftLength - 1; i >= 0; i--) + for (int i = left.Length - 1; i >= 0; i--) { - ulong value = (carry << 32) | Unsafe.Add(ref left, i); + ulong value = (carry << 32) | Unsafe.Add(ref GetReference(left), i); ulong digit = value / right; Unsafe.Add(ref quotient, i) = (uint)digit; carry = value - digit * right; @@ -54,43 +52,32 @@ public static uint[] Divide(uint[] left, uint right) uint[] quotient = new uint[left.Length]; - Divide(ref GetArrayDataReference(left), left.Length, - right, - ref GetArrayDataReference(quotient)); + Divide(left, right, ref GetArrayDataReference(quotient)); return quotient; } - private static void Divide(ref uint left, int leftLength, - uint right, - ref uint quotient) + private static void Divide(ReadOnlySpan left, uint right, ref uint quotient) { ulong carry = 0UL; - for (int i = leftLength - 1; i >= 0; i--) + for (int i = left.Length - 1; i >= 0; i--) { - ulong value = (carry << 32) | Unsafe.Add(ref left, i); + ulong value = (carry << 32) | Unsafe.Add(ref GetReference(left), i); ulong digit = value / right; Unsafe.Add(ref quotient, i) = (uint)digit; carry = value - digit * right; } } - public static uint Remainder(uint[] left, uint right) + public static uint Remainder(ReadOnlySpan left, uint right) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); // Same as above, but only computing the remainder. - return Remainder(ref GetArrayDataReference(left), left.Length, right); - } - - private static uint Remainder(ref uint left, int leftLength, - uint right) - { ulong carry = 0UL; - for (int i = leftLength - 1; i >= 0; i--) + for (int i = left.Length - 1; i >= 0; i--) { - ulong value = (carry << 32) | Unsafe.Add(ref left, i); + ulong value = (carry << 32) | Unsafe.Add(ref GetReference(left), i); carry = value % right; } @@ -114,9 +101,7 @@ public static uint[] Divide(uint[] left, uint[] right, uint[] localLeft = CreateCopy(left); uint[] bits = new uint[left.Length - right.Length + 1]; - Divide(ref GetArrayDataReference(localLeft), localLeft.Length, - ref GetArrayDataReference(right), right.Length, - ref GetArrayDataReference(bits), bits.Length); + Divide(localLeft, right, bits); remainder = localLeft; @@ -133,19 +118,16 @@ public static uint[] Divide(uint[] left, uint[] right) // Same as above, but only returning the quotient. - // NOTE: left will get overwritten, we need a local copy - - uint[] localLeft = CreateCopy(left); uint[] bits = new uint[left.Length - right.Length + 1]; - Divide(ref GetArrayDataReference(localLeft), localLeft.Length, - ref GetArrayDataReference(right), right.Length, - ref GetArrayDataReference(bits), bits.Length); + // NOTE: left will get overwritten, we need a local copy + + Divide(CreateCopy(left), right, bits); return bits; } - public static unsafe uint[] Remainder(uint[] left, uint[] right) + public static uint[] Remainder(uint[] left, uint[] right) { Debug.Assert(left != null); Debug.Assert(right != null); @@ -159,30 +141,26 @@ public static unsafe uint[] Remainder(uint[] left, uint[] right) uint[] localLeft = CreateCopy(left); - Divide(ref GetArrayDataReference(localLeft), localLeft.Length, - ref GetArrayDataReference(right), right.Length, - ref Unsafe.AsRef(null), 0); + Divide(localLeft, right, default); return localLeft; } - private static void Divide(ref uint left, int leftLength, - ref uint right, int rightLength, - ref uint bits, int bitsLength) + private static void Divide(Span left, ReadOnlySpan right, Span bits) { - Debug.Assert(leftLength >= 1); - Debug.Assert(rightLength >= 1); - Debug.Assert(leftLength >= rightLength); - Debug.Assert(bitsLength == leftLength - rightLength + 1 - || bitsLength == 0); + Debug.Assert(left.Length >= 1); + Debug.Assert(right.Length >= 1); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(bits.Length == left.Length - right.Length + 1 + || bits.Length == 0); // Executes the "grammar-school" algorithm for computing q = a / b. // Before calculating q_i, we get more bits into the highest bit // block of the divisor. Thus, guessing digits of the quotient // will be more precise. Additionally we'll get r = a % b. - uint divHi = Unsafe.Add(ref right, rightLength - 1); - uint divLo = rightLength > 1 ? Unsafe.Add(ref right, rightLength - 2) : 0; + uint divHi = Unsafe.Add(ref GetReference(right), right.Length - 1); + uint divLo = right.Length > 1 ? Unsafe.Add(ref GetReference(right), right.Length - 2) : 0; // We measure the leading zeros of the divisor int shift = LeadingZeros(divHi); @@ -191,7 +169,7 @@ private static void Divide(ref uint left, int leftLength, // And, we make sure the most significant bit is set if (shift > 0) { - uint divNx = rightLength > 2 ? Unsafe.Add(ref right, rightLength - 3) : 0; + uint divNx = right.Length > 2 ? Unsafe.Add(ref GetReference(right), right.Length - 3) : 0; divHi = (divHi << shift) | (divLo >> backShift); divLo = (divLo << shift) | (divNx >> backShift); @@ -199,18 +177,18 @@ private static void Divide(ref uint left, int leftLength, // Then, we divide all of the bits as we would do it using // pen and paper: guessing the next digit, subtracting, ... - for (int i = leftLength; i >= rightLength; i--) + for (int i = left.Length; i >= right.Length; i--) { - int n = i - rightLength; - uint t = i < leftLength ? Unsafe.Add(ref left, i) : 0; + int n = i - right.Length; + uint t = i < left.Length ? Unsafe.Add(ref GetReference(left), i) : 0; - ulong valHi = ((ulong)t << 32) | Unsafe.Add(ref left, i - 1); - uint valLo = i > 1 ? Unsafe.Add(ref left, i - 2) : 0; + ulong valHi = ((ulong)t << 32) | Unsafe.Add(ref GetReference(left), i - 1); + uint valLo = i > 1 ? Unsafe.Add(ref GetReference(left), i - 2) : 0; // We shifted the divisor, we shift the dividend too if (shift > 0) { - uint valNx = i > 2 ? Unsafe.Add(ref left, i - 3) : 0; + uint valNx = i > 2 ? Unsafe.Add(ref GetReference(left), i - 3) : 0; valHi = (valHi << shift) | (valLo >> backShift); valLo = (valLo << shift) | (valNx >> backShift); @@ -229,15 +207,13 @@ private static void Divide(ref uint left, int leftLength, if (digit > 0) { // Now it's time to subtract our current quotient - uint carry = SubtractDivisor(ref Unsafe.Add(ref left, n), leftLength - n, - ref right, rightLength, digit); + uint carry = SubtractDivisor(left.Slice(n), right, digit); if (carry != t) { Debug.Assert(carry == t + 1); // Our guess was still exactly one too high - carry = AddDivisor(ref Unsafe.Add(ref left, n), leftLength - n, - ref right, rightLength); + carry = AddDivisor(left.Slice(n), right); --digit; Debug.Assert(carry == 1); @@ -245,28 +221,27 @@ private static void Divide(ref uint left, int leftLength, } // We have the digit! - if (bitsLength != 0) - Unsafe.Add(ref bits, n) = (uint)digit; - if (i < leftLength) - Unsafe.Add(ref left, i) = 0; + if (!bits.IsEmpty) + Unsafe.Add(ref GetReference(bits), n) = (uint)digit; + if (i < left.Length) + Unsafe.Add(ref GetReference(left), i) = 0; } } - private static uint AddDivisor(ref uint left, int leftLength, - ref uint right, int rightLength) + private static uint AddDivisor(Span left, ReadOnlySpan right) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); + Debug.Assert(left.Length >= 0); + Debug.Assert(right.Length >= 0); + Debug.Assert(left.Length >= right.Length); // Repairs the dividend, if the last subtract was too much ulong carry = 0UL; - for (int i = 0; i < rightLength; i++) + for (int i = 0; i < right.Length; i++) { - ref uint leftElement = ref Unsafe.Add(ref left, i); - ulong digit = (leftElement + carry) + Unsafe.Add(ref right, i); + ref uint leftElement = ref Unsafe.Add(ref GetReference(left), i); + ulong digit = (leftElement + carry) + Unsafe.Add(ref GetReference(right), i); leftElement = unchecked((uint)digit); carry = digit >> 32; } @@ -274,13 +249,11 @@ private static uint AddDivisor(ref uint left, int leftLength, return (uint)carry; } - private static uint SubtractDivisor(ref uint left, int leftLength, - ref uint right, int rightLength, - ulong q) + private static uint SubtractDivisor(Span left, ReadOnlySpan right, ulong q) { - Debug.Assert(leftLength >= 0); - Debug.Assert(rightLength >= 0); - Debug.Assert(leftLength >= rightLength); + Debug.Assert(left.Length >= 0); + Debug.Assert(right.Length >= 0); + Debug.Assert(left.Length >= right.Length); Debug.Assert(q <= 0xFFFFFFFF); // Combines a subtract and a multiply operation, which is naturally @@ -288,12 +261,12 @@ private static uint SubtractDivisor(ref uint left, int leftLength, ulong carry = 0UL; - for (int i = 0; i < rightLength; i++) + for (int i = 0; i < right.Length; i++) { - carry += Unsafe.Add(ref right, i) * q; + carry += Unsafe.Add(ref GetReference(right), i) * q; uint digit = unchecked((uint)carry); carry = carry >> 32; - ref uint leftElement = ref Unsafe.Add(ref left, i); + ref uint leftElement = ref Unsafe.Add(ref GetReference(left), i); if (leftElement < digit) ++carry; leftElement = unchecked(leftElement - digit); From d89cdea26bbba1979127494102e812ef19c5e1fe Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 28 Apr 2020 20:53:49 +0300 Subject: [PATCH 06/88] Minus two array allocations for each bitwise operation --- .../src/System/Numerics/BigInteger.cs | 61 +++++++++++-------- .../src/System/Numerics/NumericsHelpers.cs | 18 ++++-- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 123453cdfaf21..7c1704c1f9114 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -5,6 +5,8 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.CompilerServices; +using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { @@ -18,6 +20,9 @@ namespace System.Numerics private const int DecimalScaleFactorMask = 0x00FF0000; private const int DecimalSignMask = unchecked((int)0x80000000); + //Used to create Span with single element + private static uint s_zero = 0; + // For values int.MinValue < n <= int.MaxValue, the value is stored in sign // and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits internal readonly int _sign; // Do not rename (binary serialization) @@ -1412,27 +1417,28 @@ private enum GetBytesMode { AllocateArray, Count, Span } /// /// Return the value of this BigInteger as a little-endian twos-complement - /// uint array, using the fewest number of uints possible. If the value is zero, + /// uint span, using the fewest number of uints possible. If the value is zero, /// return an array of one uint whose element is 0. /// /// - private uint[] ToUInt32Array() + private ReadOnlySpan ToUInt32Array() { - if (_bits == null && _sign == 0) - return new uint[] { 0 }; + if (_bits is null && _sign == 0) + return CreateReadOnlySpan(ref s_zero, 1); - uint[] dwords; + ReadOnlySpan dwords; uint highDWord; - if (_bits == null) + if (_bits is null) { - dwords = new uint[] { unchecked((uint)_sign) }; + dwords = CreateReadOnlySpan(ref Unsafe.As(ref Unsafe.AsRef(in _sign)), 1); highDWord = (_sign < 0) ? uint.MaxValue : 0; } else if (_sign == -1) { - dwords = (uint[])_bits.Clone(); - NumericsHelpers.DangerousMakeTwosComplement(dwords); // Mutates dwords + Span dwordsMutable = (uint[])_bits.Clone(); + NumericsHelpers.DangerousMakeTwosComplement(dwordsMutable); // Mutates dwords + dwords = dwordsMutable; highDWord = uint.MaxValue; } else @@ -1445,15 +1451,18 @@ private uint[] ToUInt32Array() int msb; for (msb = dwords.Length - 1; msb > 0; msb--) { - if (dwords[msb] != highDWord) break; + if (Unsafe.Add(ref GetReference(dwords), msb) != highDWord) break; } // Ensure high bit is 0 if positive, 1 if negative - bool needExtraByte = (dwords[msb] & 0x80000000) != (highDWord & 0x80000000); + bool needExtraByte = (Unsafe.Add(ref GetReference(dwords), msb) & 0x80000000) != (highDWord & 0x80000000); - uint[] trimmed = new uint[msb + 1 + (needExtraByte ? 1 : 0)]; - Array.Copy(dwords, trimmed, msb + 1); + Span trimmed = new uint[msb + 1 + (needExtraByte ? 1 : 0)]; + dwords = dwords.Slice(0, msb + 1); + dwords.CopyTo(trimmed); + + if (needExtraByte) + Unsafe.Add(ref GetReference(trimmed), trimmed.Length - 1) = highDWord; - if (needExtraByte) trimmed[trimmed.Length - 1] = highDWord; return trimmed; } @@ -1821,16 +1830,16 @@ public static explicit operator decimal(BigInteger value) return left._sign & right._sign; } - uint[] x = left.ToUInt32Array(); - uint[] y = right.ToUInt32Array(); + ReadOnlySpan x = left.ToUInt32Array(); + ReadOnlySpan y = right.ToUInt32Array(); uint[] z = new uint[Math.Max(x.Length, y.Length)]; uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? x[i] : xExtend; - uint yu = (i < y.Length) ? y[i] : yExtend; + uint xu = (i < x.Length) ? Unsafe.Add(ref GetReference(x), i) : xExtend; + uint yu = (i < y.Length) ? Unsafe.Add(ref GetReference(y), i) : yExtend; z[i] = xu & yu; } return new BigInteger(z); @@ -1848,16 +1857,16 @@ public static explicit operator decimal(BigInteger value) return left._sign | right._sign; } - uint[] x = left.ToUInt32Array(); - uint[] y = right.ToUInt32Array(); + ReadOnlySpan x = left.ToUInt32Array(); + ReadOnlySpan y = right.ToUInt32Array(); uint[] z = new uint[Math.Max(x.Length, y.Length)]; uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? x[i] : xExtend; - uint yu = (i < y.Length) ? y[i] : yExtend; + uint xu = (i < x.Length) ? Unsafe.Add(ref GetReference(x), i) : xExtend; + uint yu = (i < y.Length) ? Unsafe.Add(ref GetReference(y), i) : yExtend; z[i] = xu | yu; } return new BigInteger(z); @@ -1870,16 +1879,16 @@ public static explicit operator decimal(BigInteger value) return left._sign ^ right._sign; } - uint[] x = left.ToUInt32Array(); - uint[] y = right.ToUInt32Array(); + ReadOnlySpan x = left.ToUInt32Array(); + ReadOnlySpan y = right.ToUInt32Array(); uint[] z = new uint[Math.Max(x.Length, y.Length)]; uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? x[i] : xExtend; - uint yu = (i < y.Length) ? y[i] : yExtend; + uint xu = (i < x.Length) ? Unsafe.Add(ref GetReference(x), i) : xExtend; + uint yu = (i < y.Length) ? Unsafe.Add(ref GetReference(y), i) : yExtend; z[i] = xu ^ yu; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs index 88958839ee1e3..ddc103c607133 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.Numerics @@ -107,22 +108,27 @@ public static double GetDoubleFromParts(int sign, int exp, ulong man) // Do an in-place two's complement. "Dangerous" because it causes // a mutation and needs to be used with care for immutable types. - public static void DangerousMakeTwosComplement(uint[] d) + public static void DangerousMakeTwosComplement(Span d) { - if (d != null && d.Length > 0) + if (d.Length > 0) { - d[0] = unchecked(~d[0] + 1); + ref uint firstElement = ref MemoryMarshal.GetReference(d); + firstElement = unchecked(~firstElement + 1); + ref uint lookup = ref firstElement; int i = 1; + // first do complement and +1 as long as carry is needed - for (; d[i - 1] == 0 && i < d.Length; i++) + for (; Unsafe.Add(ref firstElement, i - 1) == 0 && i < d.Length; i++) { - d[i] = unchecked(~d[i] + 1); + lookup = ref Unsafe.Add(ref firstElement, i); + lookup = unchecked(~lookup + 1); } // now ones complement is sufficient for (; i < d.Length; i++) { - d[i] = ~d[i]; + lookup = ref Unsafe.Add(ref firstElement, i); + lookup = ~lookup; } } } From 7a6a0d8f700687af11bebaf929e5c279242cd13a Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 29 Apr 2020 13:08:13 +0300 Subject: [PATCH 07/88] Removed redundant locals --- .../src/System/Numerics/BigInteger.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 7c1704c1f9114..f85a65f419a73 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1426,19 +1426,18 @@ private ReadOnlySpan ToUInt32Array() if (_bits is null && _sign == 0) return CreateReadOnlySpan(ref s_zero, 1); - ReadOnlySpan dwords; + Span dwords; //don't mutate after initialization!!! uint highDWord; if (_bits is null) { - dwords = CreateReadOnlySpan(ref Unsafe.As(ref Unsafe.AsRef(in _sign)), 1); + dwords = CreateSpan(ref Unsafe.As(ref Unsafe.AsRef(in _sign)), 1); highDWord = (_sign < 0) ? uint.MaxValue : 0; } else if (_sign == -1) { - Span dwordsMutable = (uint[])_bits.Clone(); - NumericsHelpers.DangerousMakeTwosComplement(dwordsMutable); // Mutates dwords - dwords = dwordsMutable; + dwords = Unsafe.As(_bits.Clone()); + NumericsHelpers.DangerousMakeTwosComplement(dwords); // Mutates dwords highDWord = uint.MaxValue; } else From ba2a01ebe760e9af742f6998b35c6229397a242b Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 29 Apr 2020 15:43:31 +0300 Subject: [PATCH 08/88] Code review feedback (code style) --- .../src/System/Numerics/BigInteger.cs | 8 ++++---- .../Numerics/BigIntegerCalculator.BitsBuffer.cs | 4 +--- .../Numerics/BigIntegerCalculator.DivRem.cs | 12 +++++------- .../Numerics/BigIntegerCalculator.FastReducer.cs | 16 ++++++++-------- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index f85a65f419a73..821ffe8979f3d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -20,8 +20,8 @@ namespace System.Numerics private const int DecimalScaleFactorMask = 0x00FF0000; private const int DecimalSignMask = unchecked((int)0x80000000); - //Used to create Span with single element - private static uint s_zero = 0; + //Used to create Span with single element which is equal to zero + private static readonly uint[] s_zero = new uint[1]; // For values int.MinValue < n <= int.MaxValue, the value is stored in sign // and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits @@ -1418,13 +1418,13 @@ private enum GetBytesMode { AllocateArray, Count, Span } /// /// Return the value of this BigInteger as a little-endian twos-complement /// uint span, using the fewest number of uints possible. If the value is zero, - /// return an array of one uint whose element is 0. + /// return a span of one uint whose element is 0. /// /// private ReadOnlySpan ToUInt32Array() { if (_bits is null && _sign == 0) - return CreateReadOnlySpan(ref s_zero, 1); + return s_zero; Span dwords; //don't mutate after initialization!!! uint highDWord; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index 74fead0573287..6f32da44c5df8 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Runtime.CompilerServices; using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics @@ -45,8 +44,7 @@ public BitsBuffer(int size, uint[] value) Array.Copy(value, _bits, _length); } - public void MultiplySelf(ref BitsBuffer value, - ref BitsBuffer temp) + public void MultiplySelf(ref BitsBuffer value, ref BitsBuffer temp) { Debug.Assert(temp._length == 0); Debug.Assert(_length + value._length <= temp._bits.Length); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 07f24e49d2ac0..901923fdffe5d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -10,8 +10,7 @@ namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Divide(uint[] left, uint right, - out uint remainder) + public static uint[] Divide(uint[] left, uint right, out uint remainder) { Debug.Assert(left != null); Debug.Assert(left.Length >= 1); @@ -28,9 +27,9 @@ public static uint[] Divide(uint[] left, uint right, } private static void Divide(ReadOnlySpan left, - uint right, - ref uint quotient, - out uint remainder) + uint right, + ref uint quotient, + out uint remainder) { ulong carry = 0UL; for (int i = left.Length - 1; i >= 0; i--) @@ -84,8 +83,7 @@ public static uint Remainder(ReadOnlySpan left, uint right) return (uint)carry; } - public static uint[] Divide(uint[] left, uint[] right, - out uint[] remainder) + public static uint[] Divide(uint[] left, uint[] right, out uint[] remainder) { Debug.Assert(left != null); Debug.Assert(right != null); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index f806c6668e363..860f0131698f6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -68,8 +68,8 @@ public int Reduce(uint[] value, int length) } private static int DivMul(uint[] left, int leftLength, - uint[] right, int rightLength, - uint[] bits, int k) + uint[] right, int rightLength, + uint[] bits, int k) { Debug.Assert(left != null); Debug.Assert(left.Length >= leftLength); @@ -96,14 +96,14 @@ private static int DivMul(uint[] left, int leftLength, if (leftLength < rightLength) { Multiply(CreateSpan(ref r, rightLength), - CreateSpan(ref Unsafe.Add(ref l, k), leftLength), - CreateSpan(ref b, leftLength + rightLength)); + CreateSpan(ref Unsafe.Add(ref l, k), leftLength), + CreateSpan(ref b, leftLength + rightLength)); } else { Multiply(CreateSpan(ref Unsafe.Add(ref l, k), leftLength), - CreateSpan(ref r, rightLength), - CreateSpan(ref b, leftLength + rightLength)); + CreateSpan(ref r, rightLength), + CreateSpan(ref b, leftLength + rightLength)); } return ActualLength(bits, leftLength + rightLength); @@ -113,8 +113,8 @@ private static int DivMul(uint[] left, int leftLength, } private static int SubMod(uint[] left, int leftLength, - uint[] right, int rightLength, - uint[] modulus, int k) + uint[] right, int rightLength, + uint[] modulus, int k) { Debug.Assert(left != null); Debug.Assert(left.Length >= leftLength); From 49317c5ece2f92bab1dd5515499f8ee432481d10 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 29 Apr 2020 22:00:25 +0300 Subject: [PATCH 09/88] Code review feedback --- .../src/System/Numerics/BigInteger.cs | 22 ++-- .../Numerics/BigIntegerCalculator.AddSub.cs | 102 ++++++------------ .../BigIntegerCalculator.BitsBuffer.cs | 24 ++--- .../Numerics/BigIntegerCalculator.DivRem.cs | 100 ++++++----------- .../BigIntegerCalculator.FastReducer.cs | 27 ++--- .../Numerics/BigIntegerCalculator.SquMul.cs | 76 ++++--------- .../Numerics/BigIntegerCalculator.Utils.cs | 7 +- 7 files changed, 119 insertions(+), 239 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 821ffe8979f3d..0de4612737823 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -6,7 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; -using static System.Runtime.InteropServices.MemoryMarshal; +using System.Runtime.InteropServices; namespace System.Numerics { @@ -1431,7 +1431,7 @@ private ReadOnlySpan ToUInt32Array() if (_bits is null) { - dwords = CreateSpan(ref Unsafe.As(ref Unsafe.AsRef(in _sign)), 1); + dwords = MemoryMarshal.CreateSpan(ref Unsafe.As(ref Unsafe.AsRef(in _sign)), 1); highDWord = (_sign < 0) ? uint.MaxValue : 0; } else if (_sign == -1) @@ -1450,17 +1450,17 @@ private ReadOnlySpan ToUInt32Array() int msb; for (msb = dwords.Length - 1; msb > 0; msb--) { - if (Unsafe.Add(ref GetReference(dwords), msb) != highDWord) break; + if (dwords[msb] != highDWord) break; } // Ensure high bit is 0 if positive, 1 if negative - bool needExtraByte = (Unsafe.Add(ref GetReference(dwords), msb) & 0x80000000) != (highDWord & 0x80000000); + bool needExtraByte = (dwords[msb] & 0x80000000) != (highDWord & 0x80000000); Span trimmed = new uint[msb + 1 + (needExtraByte ? 1 : 0)]; dwords = dwords.Slice(0, msb + 1); dwords.CopyTo(trimmed); if (needExtraByte) - Unsafe.Add(ref GetReference(trimmed), trimmed.Length - 1) = highDWord; + trimmed[trimmed.Length - 1] = highDWord; return trimmed; } @@ -1837,8 +1837,8 @@ public static explicit operator decimal(BigInteger value) for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? Unsafe.Add(ref GetReference(x), i) : xExtend; - uint yu = (i < y.Length) ? Unsafe.Add(ref GetReference(y), i) : yExtend; + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; z[i] = xu & yu; } return new BigInteger(z); @@ -1864,8 +1864,8 @@ public static explicit operator decimal(BigInteger value) for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? Unsafe.Add(ref GetReference(x), i) : xExtend; - uint yu = (i < y.Length) ? Unsafe.Add(ref GetReference(y), i) : yExtend; + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; z[i] = xu | yu; } return new BigInteger(z); @@ -1886,8 +1886,8 @@ public static explicit operator decimal(BigInteger value) for (int i = 0; i < z.Length; i++) { - uint xu = (i < x.Length) ? Unsafe.Add(ref GetReference(x), i) : xExtend; - uint yu = (i < y.Length) ? Unsafe.Add(ref GetReference(y), i) : yExtend; + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; z[i] = xu ^ yu; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index d724723024f5b..aa2dc71b27bd2 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -3,16 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Runtime.CompilerServices; -using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Add(uint[] left, uint right) + public static uint[] Add(ReadOnlySpan left, uint right) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); // Executes the addition for one big and one 32-bit integer. @@ -20,40 +17,25 @@ public static uint[] Add(uint[] left, uint right) // processing the 32-bit integer, since it's a single element. uint[] bits = new uint[left.Length + 1]; + long carry = right; - Add(left, right, ref GetArrayDataReference(bits)); - - return bits; - } - - private static void Add(ReadOnlySpan left, - uint right, - ref uint bits) - { - long digit = (long)GetReference(left) + right; - bits = unchecked((uint)digit); - long carry = digit >> 32; - - for (int i = 1; i < left.Length; i++) + for (int i = 0; i < left.Length; i++) { - digit = Unsafe.Add(ref GetReference(left), i) + carry; - Unsafe.Add(ref bits, i) = unchecked((uint)digit); + long digit = left[i] + carry; + bits[i] = unchecked((uint)digit); carry = digit >> 32; } - Unsafe.Add(ref bits, left.Length) = (uint)carry; + + bits[left.Length] = (uint)carry; + + return bits; } - public static uint[] Add(uint[] left, uint[] right) + public static uint[] Add(ReadOnlySpan left, ReadOnlySpan right) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= right.Length); - // Switching to managed pointers helps sparing - // some nasty index calculations... - uint[] bits = new uint[left.Length + 1]; - Add(left, right, bits); return bits; @@ -76,17 +58,17 @@ private static void Add(ReadOnlySpan left, ReadOnlySpan right, Span< for (; i < right.Length; i++) { - long digit = (Unsafe.Add(ref GetReference(left), i) + carry) + Unsafe.Add(ref GetReference(right), i); - Unsafe.Add(ref GetReference(bits), i) = unchecked((uint)digit); + long digit = (left[i] + carry) + right[i]; + bits[i] = unchecked((uint)digit); carry = digit >> 32; } for (; i < left.Length; i++) { - long digit = Unsafe.Add(ref GetReference(left), i) + carry; - Unsafe.Add(ref GetReference(bits), i) = unchecked((uint)digit); + long digit = left[i] + carry; + bits[i] = unchecked((uint)digit); carry = digit >> 32; } - Unsafe.Add(ref GetReference(bits), i) = (uint)carry; + bits[i] = (uint)carry; } private static void AddSelf(Span left, ReadOnlySpan right) @@ -101,17 +83,16 @@ private static void AddSelf(Span left, ReadOnlySpan right) int i = 0; long carry = 0L; - ref uint leftElement = ref NullRef; for (; i < right.Length; i++) { - leftElement = ref Unsafe.Add(ref GetReference(left), i); - long digit = (leftElement + carry) + Unsafe.Add(ref GetReference(right), i); + ref uint leftElement = ref left[i]; + long digit = (leftElement + carry) + right[i]; leftElement = unchecked((uint)digit); carry = digit >> 32; } for (; carry != 0 && i < left.Length; i++) { - leftElement = ref Unsafe.Add(ref GetReference(left), i); + ref uint leftElement = ref left[i]; long digit = leftElement + carry; leftElement = (uint)digit; carry = digit >> 32; @@ -120,9 +101,8 @@ private static void AddSelf(Span left, ReadOnlySpan right) Debug.Assert(carry == 0); } - public static uint[] Subtract(uint[] left, uint right) + public static uint[] Subtract(ReadOnlySpan left, uint right) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); Debug.Assert(left[0] >= right || left.Length >= 2); @@ -131,38 +111,24 @@ public static uint[] Subtract(uint[] left, uint right) // processing the 32-bit integer, since it's a single element. uint[] bits = new uint[left.Length]; + long carry = -right; - Subtract(left, right, ref GetArrayDataReference(bits)); - - return bits; - } - - private static void Subtract(ReadOnlySpan left, uint right, ref uint bits) - { - long digit = (long)GetReference(left) - right; - bits = unchecked((uint)digit); - long carry = digit >> 32; - - for (int i = 1; i < left.Length; i++) + for (int i = 0; i < left.Length; i++) { - digit = Unsafe.Add(ref GetReference(left), i) + carry; - Unsafe.Add(ref bits, i) = unchecked((uint)digit); + long digit = left[i] + carry; + bits[i] = unchecked((uint)digit); carry = digit >> 32; } + + return bits; } - public static uint[] Subtract(uint[] left, uint[] right) + public static uint[] Subtract(ReadOnlySpan left, ReadOnlySpan right) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= right.Length); Debug.Assert(Compare(left, right) >= 0); - // Switching to managed pointers helps sparing - // some nasty index calculations... - uint[] bits = new uint[left.Length]; - Subtract(left, right, bits); return bits; @@ -186,14 +152,14 @@ private static void Subtract(ReadOnlySpan left, ReadOnlySpan right, for (; i < right.Length; i++) { - long digit = (Unsafe.Add(ref GetReference(left), i) + carry) - Unsafe.Add(ref GetReference(right), i); - Unsafe.Add(ref GetReference(bits), i) = unchecked((uint)digit); + long digit = (left[i] + carry) - right[i]; + bits[i] = unchecked((uint)digit); carry = digit >> 32; } for (; i < left.Length; i++) { - long digit = Unsafe.Add(ref GetReference(left), i) + carry; - Unsafe.Add(ref GetReference(bits), i) = (uint)digit; + long digit = left[i] + carry; + bits[i] = (uint)digit; carry = digit >> 32; } @@ -213,17 +179,17 @@ private static void SubtractSelf(Span left, ReadOnlySpan right) int i = 0; long carry = 0L; - ref uint leftElement = ref NullRef; + for (; i < right.Length; i++) { - leftElement = ref Unsafe.Add(ref GetReference(left), i); - long digit = (leftElement + carry) - Unsafe.Add(ref GetReference(right), i); + ref uint leftElement = ref left[i]; + long digit = (leftElement + carry) - right[i]; leftElement = unchecked((uint)digit); carry = digit >> 32; } for (; carry != 0 && i < left.Length; i++) { - leftElement = ref Unsafe.Add(ref GetReference(left), i); + ref uint leftElement = ref left[i]; long digit = leftElement + carry; leftElement = (uint)digit; carry = digit >> 32; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index 6f32da44c5df8..d648dc0db238a 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { @@ -52,21 +51,17 @@ public void MultiplySelf(ref BitsBuffer value, ref BitsBuffer temp) // Executes a multiplication for this and value, writes the // result to temp. Switches this and temp arrays afterwards. - ref uint b = ref GetArrayDataReference(_bits); - ref uint v = ref GetArrayDataReference(value._bits); - ref uint t = ref GetArrayDataReference(temp._bits); - if (_length < value._length) { - Multiply(CreateSpan(ref v, value._length), - CreateSpan(ref b, _length), - CreateSpan(ref t, _length + value._length)); + Multiply(new ReadOnlySpan(value._bits, 0, value._length), + new ReadOnlySpan(_bits, 0, _length), + new Span(temp._bits, 0, _length + value._length)); } else { - Multiply(CreateSpan(ref b, _length), - CreateSpan(ref v, value._length), - CreateSpan(ref t, _length + value._length)); + Multiply(new ReadOnlySpan(_bits, 0, _length), + new ReadOnlySpan(value._bits, 0, value._length), + new Span(temp._bits, 0, _length + value._length)); } Apply(ref temp, _length + value._length); @@ -80,11 +75,8 @@ public void SquareSelf(ref BitsBuffer temp) // Executes a square for this, writes the result to temp. // Switches this and temp arrays afterwards. - ref uint b = ref GetArrayDataReference(_bits); - ref uint t = ref GetArrayDataReference(temp._bits); - - Square(CreateSpan(ref b, _length), - CreateSpan(ref t, _length + _length)); + Square(new ReadOnlySpan(_bits, 0, _length), + new Span(temp._bits, 0, _length + _length)); Apply(ref temp, _length + _length); } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 901923fdffe5d..b93d7915a7e98 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -3,16 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Runtime.CompilerServices; -using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Divide(uint[] left, uint right, out uint remainder) + public static uint[] Divide(ReadOnlySpan left, uint right, out uint remainder) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); // Executes the division for one big and one 32-bit integer. @@ -20,52 +17,38 @@ public static uint[] Divide(uint[] left, uint right, out uint remainder) // processing the 32-bit integer, since it's a single element. uint[] quotient = new uint[left.Length]; - - Divide(left, right, ref GetArrayDataReference(quotient), out remainder); - - return quotient; - } - - private static void Divide(ReadOnlySpan left, - uint right, - ref uint quotient, - out uint remainder) - { ulong carry = 0UL; + for (int i = left.Length - 1; i >= 0; i--) { - ulong value = (carry << 32) | Unsafe.Add(ref GetReference(left), i); + ulong value = (carry << 32) | left[i]; ulong digit = value / right; - Unsafe.Add(ref quotient, i) = (uint)digit; + quotient[i] = (uint)digit; carry = value - digit * right; } remainder = (uint)carry; + + return quotient; } - public static uint[] Divide(uint[] left, uint right) + public static uint[] Divide(ReadOnlySpan left, uint right) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); // Same as above, but only computing the quotient. uint[] quotient = new uint[left.Length]; - Divide(left, right, ref GetArrayDataReference(quotient)); - - return quotient; - } - - private static void Divide(ReadOnlySpan left, uint right, ref uint quotient) - { ulong carry = 0UL; for (int i = left.Length - 1; i >= 0; i--) { - ulong value = (carry << 32) | Unsafe.Add(ref GetReference(left), i); + ulong value = (carry << 32) | left[i]; ulong digit = value / right; - Unsafe.Add(ref quotient, i) = (uint)digit; + quotient[i] = (uint)digit; carry = value - digit * right; } + + return quotient; } public static uint Remainder(ReadOnlySpan left, uint right) @@ -76,27 +59,22 @@ public static uint Remainder(ReadOnlySpan left, uint right) ulong carry = 0UL; for (int i = left.Length - 1; i >= 0; i--) { - ulong value = (carry << 32) | Unsafe.Add(ref GetReference(left), i); + ulong value = (carry << 32) | left[i]; carry = value % right; } return (uint)carry; } - public static uint[] Divide(uint[] left, uint[] right, out uint[] remainder) + public static uint[] Divide(ReadOnlySpan left, ReadOnlySpan right, out uint[] remainder) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); - // Switching to managed pointers helps sparing - // some nasty index calculations... - // NOTE: left will get overwritten, we need a local copy - uint[] localLeft = CreateCopy(left); + uint[] localLeft = left.ToArray(); uint[] bits = new uint[left.Length - right.Length + 1]; Divide(localLeft, right, bits); @@ -106,10 +84,8 @@ public static uint[] Divide(uint[] left, uint[] right, out uint[] remainder) return bits; } - public static uint[] Divide(uint[] left, uint[] right) + public static uint[] Divide(ReadOnlySpan left, ReadOnlySpan right) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); @@ -120,15 +96,13 @@ public static uint[] Divide(uint[] left, uint[] right) // NOTE: left will get overwritten, we need a local copy - Divide(CreateCopy(left), right, bits); + Divide(left.ToArray(), right, bits); return bits; } - public static uint[] Remainder(uint[] left, uint[] right) + public static uint[] Remainder(ReadOnlySpan left, ReadOnlySpan right) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); @@ -137,7 +111,7 @@ public static uint[] Remainder(uint[] left, uint[] right) // NOTE: left will get overwritten, we need a local copy - uint[] localLeft = CreateCopy(left); + uint[] localLeft = left.ToArray(); Divide(localLeft, right, default); @@ -157,8 +131,8 @@ private static void Divide(Span left, ReadOnlySpan right, Span // block of the divisor. Thus, guessing digits of the quotient // will be more precise. Additionally we'll get r = a % b. - uint divHi = Unsafe.Add(ref GetReference(right), right.Length - 1); - uint divLo = right.Length > 1 ? Unsafe.Add(ref GetReference(right), right.Length - 2) : 0; + uint divHi = right[right.Length - 1]; + uint divLo = right.Length > 1 ? right[right.Length - 2] : 0; // We measure the leading zeros of the divisor int shift = LeadingZeros(divHi); @@ -167,7 +141,7 @@ private static void Divide(Span left, ReadOnlySpan right, Span // And, we make sure the most significant bit is set if (shift > 0) { - uint divNx = right.Length > 2 ? Unsafe.Add(ref GetReference(right), right.Length - 3) : 0; + uint divNx = right.Length > 2 ? right[right.Length - 3] : 0; divHi = (divHi << shift) | (divLo >> backShift); divLo = (divLo << shift) | (divNx >> backShift); @@ -178,15 +152,15 @@ private static void Divide(Span left, ReadOnlySpan right, Span for (int i = left.Length; i >= right.Length; i--) { int n = i - right.Length; - uint t = i < left.Length ? Unsafe.Add(ref GetReference(left), i) : 0; + uint t = (uint)i < (uint)left.Length ? left[i] : 0; - ulong valHi = ((ulong)t << 32) | Unsafe.Add(ref GetReference(left), i - 1); - uint valLo = i > 1 ? Unsafe.Add(ref GetReference(left), i - 2) : 0; + ulong valHi = ((ulong)t << 32) | left[i - 1]; + uint valLo = i > 1 ? left[i - 2] : 0; // We shifted the divisor, we shift the dividend too if (shift > 0) { - uint valNx = i > 2 ? Unsafe.Add(ref GetReference(left), i - 3) : 0; + uint valNx = i > 2 ? left[i - 3] : 0; valHi = (valHi << shift) | (valLo >> backShift); valLo = (valLo << shift) | (valNx >> backShift); @@ -220,9 +194,9 @@ private static void Divide(Span left, ReadOnlySpan right, Span // We have the digit! if (!bits.IsEmpty) - Unsafe.Add(ref GetReference(bits), n) = (uint)digit; - if (i < left.Length) - Unsafe.Add(ref GetReference(left), i) = 0; + bits[n] = (uint)digit; + if ((uint)i < (uint)left.Length) + left[i] = 0; } } @@ -238,8 +212,8 @@ private static uint AddDivisor(Span left, ReadOnlySpan right) for (int i = 0; i < right.Length; i++) { - ref uint leftElement = ref Unsafe.Add(ref GetReference(left), i); - ulong digit = (leftElement + carry) + Unsafe.Add(ref GetReference(right), i); + ref uint leftElement = ref left[i]; + ulong digit = (leftElement + carry) + right[i]; leftElement = unchecked((uint)digit); carry = digit >> 32; } @@ -261,10 +235,10 @@ private static uint SubtractDivisor(Span left, ReadOnlySpan right, u for (int i = 0; i < right.Length; i++) { - carry += Unsafe.Add(ref GetReference(right), i) * q; + carry += right[i] * q; uint digit = unchecked((uint)carry); carry = carry >> 32; - ref uint leftElement = ref Unsafe.Add(ref GetReference(left), i); + ref uint leftElement = ref left[i]; if (leftElement < digit) ++carry; leftElement = unchecked(leftElement - digit); @@ -302,16 +276,6 @@ private static bool DivideGuessTooBig(ulong q, ulong valHi, uint valLo, return false; } - private static uint[] CreateCopy(uint[] value) - { - Debug.Assert(value != null); - Debug.Assert(value.Length != 0); - - uint[] bits = new uint[value.Length]; - Array.Copy(value, bits, bits.Length); - return bits; - } - private static int LeadingZeros(uint value) { if (value == 0) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 860f0131698f6..f43b9ed8cb7a6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { @@ -89,21 +88,17 @@ private static int DivMul(uint[] left, int leftLength, { leftLength -= k; - ref uint l = ref GetArrayDataReference(left); - ref uint r = ref GetArrayDataReference(right); - ref uint b = ref GetArrayDataReference(bits); - if (leftLength < rightLength) { - Multiply(CreateSpan(ref r, rightLength), - CreateSpan(ref Unsafe.Add(ref l, k), leftLength), - CreateSpan(ref b, leftLength + rightLength)); + Multiply(new ReadOnlySpan(right, 0, rightLength), + new ReadOnlySpan(left, k, leftLength), + new Span(bits, 0, leftLength + rightLength)); } else { - Multiply(CreateSpan(ref Unsafe.Add(ref l, k), leftLength), - CreateSpan(ref r, rightLength), - CreateSpan(ref b, leftLength + rightLength)); + Multiply(new ReadOnlySpan(left, k, leftLength), + new ReadOnlySpan(right, 0, rightLength), + new Span(bits, 0, leftLength + rightLength)); } return ActualLength(bits, leftLength + rightLength); @@ -131,16 +126,12 @@ private static int SubMod(uint[] left, int leftLength, if (rightLength > k) rightLength = k; - ref uint l = ref GetArrayDataReference(left); - ref uint r = ref GetArrayDataReference(right); - ref uint m = ref GetArrayDataReference(modulus); - - SubtractSelf(CreateSpan(ref l, leftLength), CreateSpan(ref r, rightLength)); + SubtractSelf(new Span(left, 0, leftLength), new ReadOnlySpan(right, 0, rightLength)); leftLength = ActualLength(left, leftLength); - while (Compare(CreateSpan(ref l, leftLength), CreateSpan(ref m, modulus.Length)) >= 0) + while (Compare(new ReadOnlySpan(left, 0, leftLength), modulus) >= 0) { - SubtractSelf(CreateSpan(ref l, leftLength), CreateSpan(ref m, modulus.Length)); + SubtractSelf(new Span(left, 0, leftLength), modulus); leftLength = ActualLength(left, leftLength); } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 38ab2e7db7e27..9e74a05df3917 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -4,21 +4,14 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Square(uint[] value) + public static uint[] Square(ReadOnlySpan value) { - Debug.Assert(value != null); - - // Switching to unsafe pointers helps sparing - // some nasty index calculations... - uint[] bits = new uint[value.Length + value.Length]; - Square(value, bits); return bits; @@ -55,23 +48,19 @@ private static void Square(ReadOnlySpan value, Span bits) // = 2^64 - 1 (which perfectly matches with ulong!). But // here we would need an UInt65... Hence, we split these // operation and do some extra shifts. - ref uint elementPtr = ref NullRef; for (int i = 0; i < value.Length; i++) { ulong carry = 0UL; for (int j = 0; j < i; j++) { - elementPtr = ref Unsafe.Add(ref GetReference(bits), i + j); - ulong digit1 = elementPtr + carry; - ulong digit2 = (ulong)Unsafe.Add(ref GetReference(value), j) * Unsafe.Add(ref GetReference(value), i); - elementPtr = unchecked((uint)(digit1 + (digit2 << 1))); + ulong digit1 = bits[i + j] + carry; + ulong digit2 = (ulong)value[j] * value[i]; + bits[i + j] = unchecked((uint)(digit1 + (digit2 << 1))); carry = (digit2 + (digit1 >> 1)) >> 31; } - elementPtr = ref Unsafe.Add(ref GetReference(value), i); - ulong digits = (ulong)elementPtr * elementPtr + carry; - elementPtr = ref Unsafe.Add(ref GetReference(bits), i + i); - elementPtr = unchecked((uint)digits); - Unsafe.Add(ref elementPtr, 1) = (uint)(digits >> 32); + ulong digits = (ulong)value[i] * value[i] + carry; + bits[i + i] = unchecked((uint)digits); + bits[i + i + 1] = (uint)(digits >> 32); } } else @@ -145,49 +134,33 @@ static void SquareFinal(ReadOnlySpan valueHigh, ReadOnlySpan valueLo } } - public static uint[] Multiply(uint[] left, uint right) + public static uint[] Multiply(ReadOnlySpan left, uint right) { - Debug.Assert(left != null); - // Executes the multiplication for one big and one 32-bit integer. // Since every step holds the already slightly familiar equation // a_i * b + c <= 2^32 - 1 + (2^32 - 1)^2 < 2^64 - 1, // we are safe regarding to overflows. uint[] bits = new uint[left.Length + 1]; - - Multiply(ref GetArrayDataReference(left), left.Length, right, ref GetArrayDataReference(bits)); - - return bits; - } - - private static void Multiply(ref uint left, int leftLength, - uint right, - ref uint bits) - { int i = 0; ulong carry = 0UL; - for ( ; i < leftLength; i++) + for ( ; i < left.Length; i++) { - ulong digits = (ulong)Unsafe.Add(ref left, i) * right + carry; - Unsafe.Add(ref bits, i) = unchecked((uint)digits); + ulong digits = (ulong)left[i] * right + carry; + bits[i] = unchecked((uint)digits); carry = digits >> 32; } - Unsafe.Add(ref bits, i) = (uint)carry; + bits[i] = (uint)carry; + + return bits; } - public static uint[] Multiply(uint[] left, uint[] right) + public static uint[] Multiply(ReadOnlySpan left, ReadOnlySpan right) { - Debug.Assert(left != null); - Debug.Assert(right != null); Debug.Assert(left.Length >= right.Length); - // Switching to unsafe pointers helps sparing - // some nasty index calculations... - uint[] bits = new uint[left.Length + right.Length]; - Multiply(left, right, bits); return bits; @@ -221,19 +194,17 @@ private static void Multiply(ReadOnlySpan left, ReadOnlySpan right, // z_i+j + a_j * b_i + c <= 2(2^32 - 1) + (2^32 - 1)^2 = // = 2^64 - 1 (which perfectly matches with ulong!). - ref uint elementPtr = ref NullRef; for (int i = 0; i < right.Length; i++) { ulong carry = 0UL; for (int j = 0; j < left.Length; j++) { - elementPtr = ref Unsafe.Add(ref GetReference(bits), i + j); - ulong digits = elementPtr + carry - + (ulong)Unsafe.Add(ref GetReference(left), j) * Unsafe.Add(ref GetReference(right), i); + ref uint elementPtr = ref bits[i + j]; + ulong digits = elementPtr + carry + (ulong)left[j] * right[i]; elementPtr = unchecked((uint)digits); carry = digits >> 32; } - Unsafe.Add(ref GetReference(bits), i + left.Length) = (uint)carry; + bits[i + left.Length] = (uint)carry; } } else @@ -332,24 +303,23 @@ private static void SubtractCore(ReadOnlySpan left, ReadOnlySpan rig int i = 0; long carry = 0L; - ref uint elementPtr = ref NullRef; for (; i < right.Length; i++) { - elementPtr = ref Unsafe.Add(ref GetReference(core), i); - long digit = (elementPtr + carry) - Unsafe.Add(ref GetReference(left), i) - Unsafe.Add(ref GetReference(right), i); + ref uint elementPtr = ref core[i]; + long digit = (elementPtr + carry) - left[i] - right[i]; elementPtr = unchecked((uint)digit); carry = digit >> 32; } for (; i < left.Length; i++) { - elementPtr = ref Unsafe.Add(ref GetReference(core), i); - long digit = (elementPtr + carry) - Unsafe.Add(ref GetReference(left), i); + ref uint elementPtr = ref core[i]; + long digit = (elementPtr + carry) - left[i]; elementPtr = unchecked((uint)digit); carry = digit >> 32; } for (; carry != 0 && i < core.Length; i++) { - elementPtr = ref Unsafe.Add(ref GetReference(core), i); + ref uint elementPtr = ref core[i]; long digit = elementPtr + carry; elementPtr = (uint)digit; carry = digit >> 32; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs index 22f2291a3513f..160ad1779a8ad 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -1,12 +1,9 @@ using System.Runtime.CompilerServices; -using static System.Runtime.InteropServices.MemoryMarshal; namespace System.Numerics { internal static partial class BigIntegerCalculator { - private static unsafe ref uint NullRef => ref Unsafe.AsRef(null); - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Span ZeroMem(Span memory) { @@ -23,8 +20,8 @@ public static int Compare(ReadOnlySpan left, ReadOnlySpan right) for (int i = left.Length - 1; i >= 0; i--) { - uint leftElement = Unsafe.Add(ref GetReference(left), i); - uint rightElement = Unsafe.Add(ref GetReference(right), i); + uint leftElement = left[i]; + uint rightElement = right[i]; if (leftElement < rightElement) return -1; if (leftElement > rightElement) From fa47aeaffbf192940c07256c483590f6af560bab Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 29 Apr 2020 22:03:39 +0300 Subject: [PATCH 10/88] LeadingZeroes replaced with BitOperations --- .../Numerics/BigIntegerCalculator.DivRem.cs | 36 +------------------ .../Numerics/BigIntegerCalculator.GcdInv.cs | 2 +- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index b93d7915a7e98..3f7097217ea55 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -135,7 +135,7 @@ private static void Divide(Span left, ReadOnlySpan right, Span uint divLo = right.Length > 1 ? right[right.Length - 2] : 0; // We measure the leading zeros of the divisor - int shift = LeadingZeros(divHi); + int shift = BitOperations.LeadingZeroCount(divHi); int backShift = 32 - shift; // And, we make sure the most significant bit is set @@ -275,39 +275,5 @@ private static bool DivideGuessTooBig(ulong q, ulong valHi, uint valLo, return false; } - - private static int LeadingZeros(uint value) - { - if (value == 0) - return 32; - - int count = 0; - if ((value & 0xFFFF0000) == 0) - { - count += 16; - value = value << 16; - } - if ((value & 0xFF000000) == 0) - { - count += 8; - value = value << 8; - } - if ((value & 0xF0000000) == 0) - { - count += 4; - value = value << 4; - } - if ((value & 0xC0000000) == 0) - { - count += 2; - value = value << 2; - } - if ((value & 0x80000000) == 0) - { - count += 1; - } - - return count; - } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 1141378379f8a..e62f55f6f7c3d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -239,7 +239,7 @@ private static void ExtractDigits(ref BitsBuffer xBuffer, } // Use all the bits but one, see [hac] 14.58 (ii) - int z = LeadingZeros((uint)xh); + int z = BitOperations.LeadingZeroCount((uint)xh); x = ((xh << 32 + z) | (xm << z) | (xl >> 32 - z)) >> 1; y = ((yh << 32 + z) | (ym << z) | (yl >> 32 - z)) >> 1; From ea7c29b4601893f326e551b72f2f904771b303ed Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 29 Apr 2020 22:19:27 +0300 Subject: [PATCH 11/88] stackalloc in Divide without remainder --- .../Numerics/BigIntegerCalculator.DivRem.cs | 17 +++++++++++++++-- .../Numerics/BigIntegerCalculator.SquMul.cs | 1 - .../Numerics/BigIntegerCalculator.Utils.cs | 7 +++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 3f7097217ea55..69709fe2b1cad 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Buffers; using System.Diagnostics; namespace System.Numerics @@ -95,8 +96,20 @@ public static uint[] Divide(ReadOnlySpan left, ReadOnlySpan right) uint[] bits = new uint[left.Length - right.Length + 1]; // NOTE: left will get overwritten, we need a local copy - - Divide(left.ToArray(), right, bits); + // However, mutated left is not used afterwards, so use array pooling or stack alloc + if (left.Length < AllocationThreshold) + { + Span leftCopy = stackalloc uint[left.Length]; + left.CopyTo(leftCopy); + Divide(leftCopy, right, bits); + } + else + { + uint[] leftCopy = ArrayPool.Shared.Rent(left.Length); + left.CopyTo(leftCopy); + Divide(new Span(leftCopy, 0, left.Length), right, bits); + ArrayPool.Shared.Return(leftCopy); + } return bits; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 9e74a05df3917..b6708d9ad8396 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -19,7 +19,6 @@ public static uint[] Square(ReadOnlySpan value) // Mutable for unit testing... private static int SquareThreshold = 32; - private static int AllocationThreshold = 256; private static void Square(ReadOnlySpan value, Span bits) { diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs index 160ad1779a8ad..7034aa0ad82da 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -1,9 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System.Runtime.CompilerServices; namespace System.Numerics { internal static partial class BigIntegerCalculator { + // Mutable for unit testing... + private static int AllocationThreshold = 256; + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Span ZeroMem(Span memory) { From a1e50062ff053dd2001a6cef1f3bca2dcd7019f9 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 00:13:43 +0300 Subject: [PATCH 12/88] Reduced memory allocation for bitwise operations --- .../src/System/Numerics/BigInteger.cs | 149 +++++++++++------- 1 file changed, 90 insertions(+), 59 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 0de4612737823..3c0c53d1b51bb 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -14,15 +15,13 @@ namespace System.Numerics [System.Runtime.CompilerServices.TypeForwardedFrom("System.Numerics, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")] public readonly struct BigInteger : IFormattable, IComparable, IComparable, IEquatable { + private const int AllocationThreshold = 256; private const uint kuMaskHighBit = unchecked((uint)int.MinValue); private const int kcbitUint = 32; private const int kcbitUlong = 64; private const int DecimalScaleFactorMask = 0x00FF0000; private const int DecimalSignMask = unchecked((int)0x80000000); - //Used to create Span with single element which is equal to zero - private static readonly uint[] s_zero = new uint[1]; - // For values int.MinValue < n <= int.MaxValue, the value is stored in sign // and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits internal readonly int _sign; // Do not rename (binary serialization) @@ -1416,53 +1415,51 @@ private enum GetBytesMode { AllocateArray, Count, Span } } /// - /// Return the value of this BigInteger as a little-endian twos-complement - /// uint span, using the fewest number of uints possible. If the value is zero, - /// return a span of one uint whose element is 0. + /// Converts the value of this BigInteger to a little-endian twos-complement + /// uint span allocated by the caller, and shrinks it to the fewest number of uints possible. /// - /// - private ReadOnlySpan ToUInt32Array() + /// Pre-allocated buffer by the caller. + private void CopyTo(ref Span buffer) { - if (_bits is null && _sign == 0) - return s_zero; + Debug.Assert(_bits is null || _sign == 0 ? buffer.Length == 2 : buffer.Length >= _bits.Length + 1); - Span dwords; //don't mutate after initialization!!! uint highDWord; if (_bits is null) { - dwords = MemoryMarshal.CreateSpan(ref Unsafe.As(ref Unsafe.AsRef(in _sign)), 1); + buffer[0] = unchecked((uint)_sign); highDWord = (_sign < 0) ? uint.MaxValue : 0; } else if (_sign == -1) { - dwords = Unsafe.As(_bits.Clone()); - NumericsHelpers.DangerousMakeTwosComplement(dwords); // Mutates dwords + _bits.CopyTo(buffer); + NumericsHelpers.DangerousMakeTwosComplement(buffer[..^1]); // Mutates dwords highDWord = uint.MaxValue; } else { - dwords = _bits; + _bits.CopyTo(buffer); highDWord = 0; } // Find highest significant byte int msb; - for (msb = dwords.Length - 1; msb > 0; msb--) + for (msb = buffer.Length - 2; msb > 0; msb--) { - if (dwords[msb] != highDWord) break; + if (buffer[msb] != highDWord) break; } // Ensure high bit is 0 if positive, 1 if negative - bool needExtraByte = (dwords[msb] & 0x80000000) != (highDWord & 0x80000000); - - Span trimmed = new uint[msb + 1 + (needExtraByte ? 1 : 0)]; - dwords = dwords.Slice(0, msb + 1); - dwords.CopyTo(trimmed); + bool needExtraByte = (buffer[msb] & 0x80000000) != (highDWord & 0x80000000); if (needExtraByte) - trimmed[trimmed.Length - 1] = highDWord; - - return trimmed; + { + buffer = buffer.Slice(0, msb + 2); + buffer[buffer.Length - 1] = highDWord; + } + else + { + buffer = buffer.Slice(0, msb + 1); + } } public override string ToString() @@ -1817,6 +1814,41 @@ public static explicit operator decimal(BigInteger value) return new decimal(lo, mi, hi, value._sign < 0, 0); } + private delegate void BitwiseOperation(ReadOnlySpan left, uint leftExtend, + ReadOnlySpan right, uint rightExtend, + uint[] result); + + private static BigInteger DoBitwiseOperation(BigInteger left, BigInteger right, BitwiseOperation operation) + { + uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; + uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; + + uint[]? leftBufferFromPool = null; + int size = (left._bits?.Length ?? 1) + 1; + Span x = size <= AllocationThreshold ? + stackalloc uint[size] + : leftBufferFromPool = ArrayPool.Shared.Rent(size); + left.CopyTo(ref x); + + uint[]? rightBufferFromPool = null; + size = (right._bits?.Length ?? 1) + 1; + Span y = size <= AllocationThreshold ? + stackalloc uint[size] + : rightBufferFromPool = ArrayPool.Shared.Rent(size); + right.CopyTo(ref y); + + var result = new uint[Math.Max(x.Length, y.Length)]; + operation(x, xExtend, y, yExtend, result); + + if (leftBufferFromPool != null) + ArrayPool.Shared.Return(leftBufferFromPool); + + if (rightBufferFromPool != null) + ArrayPool.Shared.Return(rightBufferFromPool); + + return new BigInteger(result); + } + public static BigInteger operator &(BigInteger left, BigInteger right) { if (left.IsZero || right.IsZero) @@ -1824,24 +1856,24 @@ public static explicit operator decimal(BigInteger value) return Zero; } - if (left._bits == null && right._bits == null) + if (left._bits is null && right._bits is null) { return left._sign & right._sign; } - ReadOnlySpan x = left.ToUInt32Array(); - ReadOnlySpan y = right.ToUInt32Array(); - uint[] z = new uint[Math.Max(x.Length, y.Length)]; - uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; - uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; + return DoBitwiseOperation(left, right, BitwiseAnd); - for (int i = 0; i < z.Length; i++) + static void BitwiseAnd(ReadOnlySpan left, uint leftExtend, + ReadOnlySpan right, uint rightExtend, + uint[] result) { - uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; - uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; - z[i] = xu & yu; + for (int i = 0; i < result.Length; i++) + { + uint xu = ((uint)i < (uint)left.Length) ? left[i] : leftExtend; + uint yu = ((uint)i < (uint)right.Length) ? right[i] : rightExtend; + result[i] = xu & yu; + } } - return new BigInteger(z); } public static BigInteger operator |(BigInteger left, BigInteger right) @@ -1851,47 +1883,46 @@ public static explicit operator decimal(BigInteger value) if (right.IsZero) return left; - if (left._bits == null && right._bits == null) + if (left._bits is null && right._bits is null) { return left._sign | right._sign; } - ReadOnlySpan x = left.ToUInt32Array(); - ReadOnlySpan y = right.ToUInt32Array(); - uint[] z = new uint[Math.Max(x.Length, y.Length)]; - uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; - uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; + return DoBitwiseOperation(left, right, BitwiseOr); - for (int i = 0; i < z.Length; i++) + static void BitwiseOr(ReadOnlySpan left, uint leftExtend, + ReadOnlySpan right, uint rightExtend, + uint[] result) { - uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; - uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; - z[i] = xu | yu; + for (int i = 0; i < result.Length; i++) + { + uint xu = ((uint)i < (uint)left.Length) ? left[i] : leftExtend; + uint yu = ((uint)i < (uint)right.Length) ? right[i] : rightExtend; + result[i] = xu | yu; + } } - return new BigInteger(z); } public static BigInteger operator ^(BigInteger left, BigInteger right) { - if (left._bits == null && right._bits == null) + if (left._bits is null && right._bits is null) { return left._sign ^ right._sign; } - ReadOnlySpan x = left.ToUInt32Array(); - ReadOnlySpan y = right.ToUInt32Array(); - uint[] z = new uint[Math.Max(x.Length, y.Length)]; - uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; - uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; + return DoBitwiseOperation(left, right, BitwiseXor); - for (int i = 0; i < z.Length; i++) + static void BitwiseXor(ReadOnlySpan left, uint leftExtend, + ReadOnlySpan right, uint rightExtend, + uint[] result) { - uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; - uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; - z[i] = xu ^ yu; + for (int i = 0; i < result.Length; i++) + { + uint xu = ((uint)i < (uint)left.Length) ? left[i] : leftExtend; + uint yu = ((uint)i < (uint)right.Length) ? right[i] : rightExtend; + result[i] = xu ^ yu; + } } - - return new BigInteger(z); } public static BigInteger operator <<(BigInteger value, int shift) From f19ff53a9d6952e6a334ffc795d81d3a80418028 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 02:32:57 +0300 Subject: [PATCH 13/88] Managed pointer replaced with Span --- .../src/System/Numerics/NumericsHelpers.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs index ddc103c607133..cd26ceb8a5e59 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.Numerics @@ -112,23 +111,19 @@ public static void DangerousMakeTwosComplement(Span d) { if (d.Length > 0) { - ref uint firstElement = ref MemoryMarshal.GetReference(d); - firstElement = unchecked(~firstElement + 1); + d[0] = unchecked(~d[0] + 1); - ref uint lookup = ref firstElement; int i = 1; // first do complement and +1 as long as carry is needed - for (; Unsafe.Add(ref firstElement, i - 1) == 0 && i < d.Length; i++) + for (; d[i - 1] == 0 && i < d.Length; i++) { - lookup = ref Unsafe.Add(ref firstElement, i); - lookup = unchecked(~lookup + 1); + d[i] = unchecked(~d[i] + 1); } // now ones complement is sufficient for (; i < d.Length; i++) { - lookup = ref Unsafe.Add(ref firstElement, i); - lookup = ~lookup; + d[i] = ~d[i]; } } } From 6e8705f2b0e1cbd8ccf4d41211a4730d261ae131 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 02:38:25 +0300 Subject: [PATCH 14/88] Removed namespace imports --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 3c0c53d1b51bb..c60bbe079b3fd 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -6,8 +6,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System.Numerics { From 34893a3dc533dcdc331d10985c973734ab7157a5 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 11:15:21 +0300 Subject: [PATCH 15/88] Removed delegate type --- .../src/System/Numerics/BigInteger.cs | 152 +++++++++++------- 1 file changed, 90 insertions(+), 62 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index c60bbe079b3fd..139c59a0a7e01 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -13,7 +13,7 @@ namespace System.Numerics [System.Runtime.CompilerServices.TypeForwardedFrom("System.Numerics, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")] public readonly struct BigInteger : IFormattable, IComparable, IComparable, IEquatable { - private const int AllocationThreshold = 256; + private const int StackAllocThreshold = 256; private const uint kuMaskHighBit = unchecked((uint)int.MinValue); private const int kcbitUint = 32; private const int kcbitUlong = 64; @@ -1414,10 +1414,11 @@ private enum GetBytesMode { AllocateArray, Count, Span } /// /// Converts the value of this BigInteger to a little-endian twos-complement - /// uint span allocated by the caller, and shrinks it to the fewest number of uints possible. + /// uint span allocated by the caller using the fewest number of uints possible. /// /// Pre-allocated buffer by the caller. - private void CopyTo(ref Span buffer) + /// The actual number of copied elements. + private int CopyTo(Span buffer) { Debug.Assert(_bits is null || _sign == 0 ? buffer.Length == 2 : buffer.Length >= _bits.Length + 1); @@ -1448,16 +1449,21 @@ private void CopyTo(ref Span buffer) } // Ensure high bit is 0 if positive, 1 if negative bool needExtraByte = (buffer[msb] & 0x80000000) != (highDWord & 0x80000000); + int count; if (needExtraByte) { - buffer = buffer.Slice(0, msb + 2); + count = msb + 2; + buffer = buffer.Slice(0, count); buffer[buffer.Length - 1] = highDWord; } else { - buffer = buffer.Slice(0, msb + 1); + count = msb + 1; + buffer = buffer.Slice(0, count); } + + return count; } public override string ToString() @@ -1812,31 +1818,42 @@ public static explicit operator decimal(BigInteger value) return new decimal(lo, mi, hi, value._sign < 0, 0); } - private delegate void BitwiseOperation(ReadOnlySpan left, uint leftExtend, - ReadOnlySpan right, uint rightExtend, - uint[] result); - - private static BigInteger DoBitwiseOperation(BigInteger left, BigInteger right, BitwiseOperation operation) + public static BigInteger operator &(BigInteger left, BigInteger right) { + if (left.IsZero || right.IsZero) + { + return Zero; + } + + if (left._bits is null && right._bits is null) + { + return left._sign & right._sign; + } + uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; uint[]? leftBufferFromPool = null; int size = (left._bits?.Length ?? 1) + 1; - Span x = size <= AllocationThreshold ? + Span x = size <= StackAllocThreshold ? stackalloc uint[size] : leftBufferFromPool = ArrayPool.Shared.Rent(size); - left.CopyTo(ref x); + x = x.Slice(0, left.CopyTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; - Span y = size <= AllocationThreshold ? + Span y = size <= StackAllocThreshold ? stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); - right.CopyTo(ref y); + y = y.Slice(0, right.CopyTo(y)); var result = new uint[Math.Max(x.Length, y.Length)]; - operation(x, xExtend, y, yExtend, result); + for (int i = 0; i < result.Length; i++) + { + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; + result[i] = xu & yu; + } if (leftBufferFromPool != null) ArrayPool.Shared.Return(leftBufferFromPool); @@ -1847,33 +1864,6 @@ stackalloc uint[size] return new BigInteger(result); } - public static BigInteger operator &(BigInteger left, BigInteger right) - { - if (left.IsZero || right.IsZero) - { - return Zero; - } - - if (left._bits is null && right._bits is null) - { - return left._sign & right._sign; - } - - return DoBitwiseOperation(left, right, BitwiseAnd); - - static void BitwiseAnd(ReadOnlySpan left, uint leftExtend, - ReadOnlySpan right, uint rightExtend, - uint[] result) - { - for (int i = 0; i < result.Length; i++) - { - uint xu = ((uint)i < (uint)left.Length) ? left[i] : leftExtend; - uint yu = ((uint)i < (uint)right.Length) ? right[i] : rightExtend; - result[i] = xu & yu; - } - } - } - public static BigInteger operator |(BigInteger left, BigInteger right) { if (left.IsZero) @@ -1886,19 +1876,38 @@ static void BitwiseAnd(ReadOnlySpan left, uint leftExtend, return left._sign | right._sign; } - return DoBitwiseOperation(left, right, BitwiseOr); + uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; + uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; - static void BitwiseOr(ReadOnlySpan left, uint leftExtend, - ReadOnlySpan right, uint rightExtend, - uint[] result) + uint[]? leftBufferFromPool = null; + int size = (left._bits?.Length ?? 1) + 1; + Span x = size <= StackAllocThreshold ? + stackalloc uint[size] + : leftBufferFromPool = ArrayPool.Shared.Rent(size); + x = x.Slice(0, left.CopyTo(x)); + + uint[]? rightBufferFromPool = null; + size = (right._bits?.Length ?? 1) + 1; + Span y = size <= StackAllocThreshold ? + stackalloc uint[size] + : rightBufferFromPool = ArrayPool.Shared.Rent(size); + y = y.Slice(0, right.CopyTo(y)); + + var result = new uint[Math.Max(x.Length, y.Length)]; + for (int i = 0; i < result.Length; i++) { - for (int i = 0; i < result.Length; i++) - { - uint xu = ((uint)i < (uint)left.Length) ? left[i] : leftExtend; - uint yu = ((uint)i < (uint)right.Length) ? right[i] : rightExtend; - result[i] = xu | yu; - } + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; + result[i] = xu | yu; } + + if (leftBufferFromPool != null) + ArrayPool.Shared.Return(leftBufferFromPool); + + if (rightBufferFromPool != null) + ArrayPool.Shared.Return(rightBufferFromPool); + + return new BigInteger(result); } public static BigInteger operator ^(BigInteger left, BigInteger right) @@ -1908,19 +1917,38 @@ static void BitwiseOr(ReadOnlySpan left, uint leftExtend, return left._sign ^ right._sign; } - return DoBitwiseOperation(left, right, BitwiseXor); + uint xExtend = (left._sign < 0) ? uint.MaxValue : 0; + uint yExtend = (right._sign < 0) ? uint.MaxValue : 0; + + uint[]? leftBufferFromPool = null; + int size = (left._bits?.Length ?? 1) + 1; + Span x = size <= StackAllocThreshold ? + stackalloc uint[size] + : leftBufferFromPool = ArrayPool.Shared.Rent(size); + x = x.Slice(0, left.CopyTo(x)); + + uint[]? rightBufferFromPool = null; + size = (right._bits?.Length ?? 1) + 1; + Span y = size <= StackAllocThreshold ? + stackalloc uint[size] + : rightBufferFromPool = ArrayPool.Shared.Rent(size); + y = y.Slice(0, right.CopyTo(y)); - static void BitwiseXor(ReadOnlySpan left, uint leftExtend, - ReadOnlySpan right, uint rightExtend, - uint[] result) + var result = new uint[Math.Max(x.Length, y.Length)]; + for (int i = 0; i < result.Length; i++) { - for (int i = 0; i < result.Length; i++) - { - uint xu = ((uint)i < (uint)left.Length) ? left[i] : leftExtend; - uint yu = ((uint)i < (uint)right.Length) ? right[i] : rightExtend; - result[i] = xu ^ yu; - } + uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; + uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; + result[i] = xu ^ yu; } + + if (leftBufferFromPool != null) + ArrayPool.Shared.Return(leftBufferFromPool); + + if (rightBufferFromPool != null) + ArrayPool.Shared.Return(rightBufferFromPool); + + return new BigInteger(result); } public static BigInteger operator <<(BigInteger value, int shift) From 59924068ef0840b923e6ed5d13c478d869868111 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 11:59:34 +0300 Subject: [PATCH 16/88] Reduced memory allocation in the end of each bitwise operation --- .../src/System/Numerics/BigInteger.cs | 92 ++++++++++--------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 139c59a0a7e01..60130cb111de2 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -510,16 +510,11 @@ internal BigInteger(uint[] value, bool negative) } /// - /// Create a BigInteger from a little-endian twos-complement UInt32 array. - /// When possible, value is assigned directly to this._bits without an array copy - /// so use this ctor with care. + /// Create a BigInteger from a little-endian twos-complement UInt32 span. /// /// - private BigInteger(uint[] value) + private BigInteger(Span value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - int dwordCount = value.Length; bool isNegative = dwordCount > 0 && ((value[dwordCount - 1] & 0x80000000) == 0x80000000); @@ -558,18 +553,10 @@ private BigInteger(uint[] value) if (!isNegative) { // Handle the simple positive value cases where the input is already in sign magnitude - if (dwordCount != value.Length) - { - _sign = +1; - _bits = new uint[dwordCount]; - Array.Copy(value, _bits, dwordCount); - } - // No trimming is possible. Assign value directly to _bits. - else - { - _sign = +1; - _bits = value; - } + _sign = +1; + value = value.Slice(0, dwordCount); + _bits = value.ToArray(); + value.CopyTo(_bits); AssertValid(); return; } @@ -598,19 +585,12 @@ private BigInteger(uint[] value) _bits = null; } } - // The number is represented by multiple dwords. - // Trim off any wasted uint values when possible. - else if (len != value.Length) - { - _sign = -1; - _bits = new uint[len]; - Array.Copy(value, _bits, len); - } - // No trimming is possible. Assign value directly to _bits. else { _sign = -1; - _bits = value; + value = value.Slice(0, len); + _bits = new uint[len]; + value.CopyTo(_bits); } AssertValid(); return; @@ -1847,12 +1827,17 @@ stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); y = y.Slice(0, right.CopyTo(y)); - var result = new uint[Math.Max(x.Length, y.Length)]; - for (int i = 0; i < result.Length; i++) + uint[]? resultBufferFromPool = null; + size = Math.Max(x.Length, y.Length); + Span z = size <= StackAllocThreshold ? + stackalloc uint[size] + : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + for (int i = 0; i < z.Length; i++) { uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; - result[i] = xu & yu; + z[i] = xu & yu; } if (leftBufferFromPool != null) @@ -1861,7 +1846,12 @@ stackalloc uint[size] if (rightBufferFromPool != null) ArrayPool.Shared.Return(rightBufferFromPool); - return new BigInteger(result); + var result = new BigInteger(z); + + if (resultBufferFromPool != null) + ArrayPool.Shared.Return(resultBufferFromPool); + + return result; } public static BigInteger operator |(BigInteger left, BigInteger right) @@ -1893,12 +1883,17 @@ stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); y = y.Slice(0, right.CopyTo(y)); - var result = new uint[Math.Max(x.Length, y.Length)]; - for (int i = 0; i < result.Length; i++) + uint[]? resultBufferFromPool = null; + size = Math.Max(x.Length, y.Length); + Span z = size <= StackAllocThreshold ? + stackalloc uint[size] + : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + for (int i = 0; i < z.Length; i++) { uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; - result[i] = xu | yu; + z[i] = xu | yu; } if (leftBufferFromPool != null) @@ -1907,7 +1902,12 @@ stackalloc uint[size] if (rightBufferFromPool != null) ArrayPool.Shared.Return(rightBufferFromPool); - return new BigInteger(result); + var result = new BigInteger(z); + + if (resultBufferFromPool != null) + ArrayPool.Shared.Return(resultBufferFromPool); + + return result; } public static BigInteger operator ^(BigInteger left, BigInteger right) @@ -1934,12 +1934,17 @@ stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); y = y.Slice(0, right.CopyTo(y)); - var result = new uint[Math.Max(x.Length, y.Length)]; - for (int i = 0; i < result.Length; i++) + uint[]? resultBufferFromPool = null; + size = Math.Max(x.Length, y.Length); + Span z = size <= StackAllocThreshold ? + stackalloc uint[size] + : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + for (int i = 0; i < z.Length; i++) { uint xu = ((uint)i < (uint)x.Length) ? x[i] : xExtend; uint yu = ((uint)i < (uint)y.Length) ? y[i] : yExtend; - result[i] = xu ^ yu; + z[i] = xu ^ yu; } if (leftBufferFromPool != null) @@ -1948,7 +1953,12 @@ stackalloc uint[size] if (rightBufferFromPool != null) ArrayPool.Shared.Return(rightBufferFromPool); - return new BigInteger(result); + var result = new BigInteger(z); + + if (resultBufferFromPool != null) + ArrayPool.Shared.Return(resultBufferFromPool); + + return result; } public static BigInteger operator <<(BigInteger value, int shift) From 2b7b7ba4d597057b8f9934619c8322b2648ebac5 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 12:08:55 +0300 Subject: [PATCH 17/88] Code review feedback --- .../Numerics/BigIntegerCalculator.DivRem.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 69709fe2b1cad..0b78b568ca1b7 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -94,22 +94,16 @@ public static uint[] Divide(ReadOnlySpan left, ReadOnlySpan right) // Same as above, but only returning the quotient. uint[] bits = new uint[left.Length - right.Length + 1]; + uint[]? leftCopyFromPool = null; // NOTE: left will get overwritten, we need a local copy // However, mutated left is not used afterwards, so use array pooling or stack alloc - if (left.Length < AllocationThreshold) - { - Span leftCopy = stackalloc uint[left.Length]; - left.CopyTo(leftCopy); - Divide(leftCopy, right, bits); - } - else - { - uint[] leftCopy = ArrayPool.Shared.Rent(left.Length); - left.CopyTo(leftCopy); - Divide(new Span(leftCopy, 0, left.Length), right, bits); - ArrayPool.Shared.Return(leftCopy); - } + Span leftCopy = left.Length < AllocationThreshold ? + stackalloc uint[left.Length] + : (leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).AsSpan(0, left.Length); + left.CopyTo(leftCopy); + + Divide(leftCopy, right, bits); return bits; } From 03313c196c78c2ebce6b0fe80cb206ba7e9179e5 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 12:12:55 +0300 Subject: [PATCH 18/88] Removed redundant array allocation --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 60130cb111de2..0040a4ea74bdc 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -589,8 +589,7 @@ private BigInteger(Span value) { _sign = -1; value = value.Slice(0, len); - _bits = new uint[len]; - value.CopyTo(_bits); + _bits = value.ToArray(); } AssertValid(); return; From 92618a40c5ff594f2b2659697ef03105f277c77a Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 13:02:23 +0300 Subject: [PATCH 19/88] Trivial division moved to stack alloc --- .../src/System/Numerics/BigInteger.cs | 22 ++++++++++++++----- .../Numerics/BigIntegerCalculator.DivRem.cs | 6 ++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 0040a4ea74bdc..d24192953c933 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -478,7 +478,7 @@ internal BigInteger(int n, uint[]? rgu) /// /// The absolute value of the number /// The bool indicating the sign of the value. - internal BigInteger(uint[] value, bool negative) + internal BigInteger(ReadOnlySpan value, bool negative) { if (value == null) throw new ArgumentNullException(nameof(value)); @@ -503,8 +503,8 @@ internal BigInteger(uint[] value, bool negative) else { _sign = negative ? -1 : +1; - _bits = new uint[len]; - Array.Copy(value, _bits, len); + value = value.Slice(0, len); + _bits = value.ToArray(); } AssertValid(); } @@ -742,10 +742,22 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big if (trivialDivisor) { uint rest; - uint[] bits = BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), out rest); + + uint[]? bitsFromPool = null; + int size = dividend._bits.Length; + Span quotient = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient, out rest); remainder = dividend._sign < 0 ? -1 * rest : rest; - return new BigInteger(bits, (dividend._sign < 0) ^ (divisor._sign < 0)); + var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } Debug.Assert(divisor._bits != null); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 0b78b568ca1b7..01422b43021dd 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -9,15 +9,15 @@ namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Divide(ReadOnlySpan left, uint right, out uint remainder) + public static void Divide(ReadOnlySpan left, uint right, Span quotient, out uint remainder) { Debug.Assert(left.Length >= 1); + Debug.Assert(quotient.Length == left.Length); // Executes the division for one big and one 32-bit integer. // Thus, we've similar code than below, but there is no loop for // processing the 32-bit integer, since it's a single element. - uint[] quotient = new uint[left.Length]; ulong carry = 0UL; for (int i = left.Length - 1; i >= 0; i--) @@ -28,8 +28,6 @@ public static uint[] Divide(ReadOnlySpan left, uint right, out uint remain carry = value - digit * right; } remainder = (uint)carry; - - return quotient; } public static uint[] Divide(ReadOnlySpan left, uint right) From 65f2701fae81d46a36d99a5e89fc962273de2c42 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 15:13:37 +0300 Subject: [PATCH 20/88] Reduced memory allocation for divide operator --- .../src/System/Numerics/BigInteger.cs | 80 +++++++++++++++---- .../Numerics/BigIntegerCalculator.DivRem.cs | 36 ++++++--- 2 files changed, 89 insertions(+), 27 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index d24192953c933..797be7e2259e3 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -749,15 +749,19 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); - BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient, out rest); - - remainder = dividend._sign < 0 ? -1 * rest : rest; - var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); - - if (bitsFromPool != null) - ArrayPool.Shared.Return(bitsFromPool); + try + { + //may throw DivideByZeroException + BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient, out rest); - return result; + remainder = dividend._sign < 0 ? -1 * rest : rest; + return new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + } + finally + { + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + } } Debug.Assert(divisor._bits != null); @@ -769,11 +773,30 @@ stackalloc uint[size] } else { - uint[] rest; - uint[] bits = BigIntegerCalculator.Divide(dividend._bits, divisor._bits, out rest); + uint[]? remainderFromPool = null; + int size = dividend._bits.Length; + Span rest = size <= StackAllocThreshold ? + stackalloc uint[size] + : (remainderFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + uint[]? quotientFromPool = null; + size = dividend._bits.Length - divisor._bits.Length + 1; + Span quotient = size <= StackAllocThreshold ? + stackalloc uint[size] + : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Divide(dividend._bits, divisor._bits, quotient, rest); remainder = new BigInteger(rest, dividend._sign < 0); - return new BigInteger(bits, (dividend._sign < 0) ^ (divisor._sign < 0)); + var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + + if (remainderFromPool != null) + ArrayPool.Shared.Return(remainderFromPool); + + if (quotientFromPool != null) + ArrayPool.Shared.Return(quotientFromPool); + + return result; } } @@ -2174,8 +2197,24 @@ stackalloc uint[size] if (trivialDivisor) { Debug.Assert(dividend._bits != null); - uint[] bits = BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign)); - return new BigInteger(bits, (dividend._sign < 0) ^ (divisor._sign < 0)); + + uint[]? quotientFromPool = null; + int size = dividend._bits.Length; + Span quotient = size <= StackAllocThreshold ? + stackalloc uint[size] + : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + try + { + //may throw DivideByZeroException + BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient); + return new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + } + finally + { + if (quotientFromPool != null) + ArrayPool.Shared.Return(quotientFromPool); + } } Debug.Assert(dividend._bits != null && divisor._bits != null); @@ -2186,8 +2225,19 @@ stackalloc uint[size] } else { - uint[] bits = BigIntegerCalculator.Divide(dividend._bits, divisor._bits); - return new BigInteger(bits, (dividend._sign < 0) ^ (divisor._sign < 0)); + uint[]? quotientFromPool = null; + int size = dividend._bits.Length - divisor._bits.Length + 1; + Span quotient = size < StackAllocThreshold ? + stackalloc uint[size] + : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Divide(dividend._bits, divisor._bits, quotient); + var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); + + if (quotientFromPool != null) + ArrayPool.Shared.Return(quotientFromPool); + + return result; } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 01422b43021dd..50acfe4c1f653 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -30,14 +30,13 @@ public static void Divide(ReadOnlySpan left, uint right, Span quotie remainder = (uint)carry; } - public static uint[] Divide(ReadOnlySpan left, uint right) + public static void Divide(ReadOnlySpan left, uint right, Span quotient) { Debug.Assert(left.Length >= 1); + Debug.Assert(quotient.Length == left.Length); // Same as above, but only computing the quotient. - uint[] quotient = new uint[left.Length]; - ulong carry = 0UL; for (int i = left.Length - 1; i >= 0; i--) { @@ -46,8 +45,6 @@ public static uint[] Divide(ReadOnlySpan left, uint right) quotient[i] = (uint)digit; carry = value - digit * right; } - - return quotient; } public static uint Remainder(ReadOnlySpan left, uint right) @@ -65,22 +62,37 @@ public static uint Remainder(ReadOnlySpan left, uint right) return (uint)carry; } - public static uint[] Divide(ReadOnlySpan left, ReadOnlySpan right, out uint[] remainder) + public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Span quotient, Span remainder) { Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); + Debug.Assert(quotient.Length == left.Length - right.Length + 1); + Debug.Assert(remainder.Length == left.Length); - // NOTE: left will get overwritten, we need a local copy + left.CopyTo(remainder); + Divide(remainder, right, quotient); + } - uint[] localLeft = left.ToArray(); - uint[] bits = new uint[left.Length - right.Length + 1]; + public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Span quotient) + { + Debug.Assert(left.Length >= 1); + Debug.Assert(right.Length >= 1); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(quotient.Length == left.Length - right.Length + 1); - Divide(localLeft, right, bits); + // Same as above, but only returning the quotient. - remainder = localLeft; + uint[]? leftCopyFromPool = null; - return bits; + // NOTE: left will get overwritten, we need a local copy + // However, mutated left is not used afterwards, so use array pooling or stack alloc + Span leftCopy = left.Length < AllocationThreshold ? + stackalloc uint[left.Length] + : (leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).AsSpan(0, left.Length); + left.CopyTo(leftCopy); + + Divide(leftCopy, right, quotient); } public static uint[] Divide(ReadOnlySpan left, ReadOnlySpan right) From b5a77b480b1cc334af6d195c2e5a8d26211ced97 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 16:17:56 +0300 Subject: [PATCH 21/88] Reduced memory allocation for shift operations --- .../src/System/Numerics/BigInteger.cs | 87 ++++++++++++------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 797be7e2259e3..18754230671f3 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -474,15 +474,12 @@ internal BigInteger(int n, uint[]? rgu) /// /// Constructor used during bit manipulation and arithmetic. - /// When possible the uint[] will be packed into _sign to conserve space. + /// When possible the uints will be packed into _sign to conserve space. /// /// The absolute value of the number /// The bool indicating the sign of the value. internal BigInteger(ReadOnlySpan value, bool negative) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - int len; // Try to conserve space as much as possible by checking for wasted leading uint[] entries @@ -2004,11 +2001,19 @@ stackalloc uint[size] int digitShift = shift / kcbitUint; int smallShift = shift - (digitShift * kcbitUint); - uint[] xd; int xl; bool negx; - negx = GetPartsForBitManipulation(ref value, out xd, out xl); + uint[]? xdFromPool = null; + int xl = value._bits?.Length ?? 1; + Span xd = xl <= StackAllocThreshold ? + stackalloc uint[xl] + : (xdFromPool = ArrayPool.Shared.Rent(xl)).AsSpan(0, xl); + bool negx = value.GetPartsForBitManipulation(xd); int zl = xl + digitShift + 1; - uint[] zd = new uint[zl]; + uint[]? zdFromPool = null; + Span zd = zl <= StackAllocThreshold ? + stackalloc uint[zl] + : (zdFromPool = ArrayPool.Shared.Rent(zl)).AsSpan(0, zl); + zd.Clear(); if (smallShift == 0) { @@ -2030,7 +2035,15 @@ stackalloc uint[size] } zd[i + digitShift] = carry; } - return new BigInteger(zd, negx); + + var result = new BigInteger(zd, negx); + + if (xdFromPool != null) + ArrayPool.Shared.Return(xdFromPool); + if (zdFromPool != null) + ArrayPool.Shared.Return(zdFromPool); + + return result; } public static BigInteger operator >>(BigInteger value, int shift) @@ -2042,24 +2055,32 @@ stackalloc uint[size] int digitShift = shift / kcbitUint; int smallShift = shift - (digitShift * kcbitUint); - uint[] xd; int xl; bool negx; - negx = GetPartsForBitManipulation(ref value, out xd, out xl); + BigInteger result; + + uint[]? xdFromPool = null; + int xl = value._bits?.Length ?? 1; + Span xd = xl <= StackAllocThreshold ? + stackalloc uint[xl] + : (xdFromPool = ArrayPool.Shared.Rent(xl)).AsSpan(0, xl); + + bool negx = value.GetPartsForBitManipulation(xd); if (negx) { if (shift >= (kcbitUint * xl)) { - return MinusOne; + result = MinusOne; + goto exit; } - uint[] temp = new uint[xl]; - Array.Copy(xd /* sourceArray */, 0 /* sourceIndex */, temp /* destinationArray */, 0 /* destinationIndex */, xl /* length */); // Make a copy of immutable value._bits - xd = temp; NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd } - int zl = xl - digitShift; - if (zl < 0) zl = 0; - uint[] zd = new uint[zl]; + uint[]? zdFromPool = null; + int zl = Math.Max(xl - digitShift, 0); + Span zd = zl <= StackAllocThreshold ? + stackalloc uint[zl] + : (zdFromPool = ArrayPool.Shared.Rent(zl)).AsSpan(0, zl); + zd.Clear(); if (smallShift == 0) { @@ -2087,7 +2108,15 @@ stackalloc uint[size] { NumericsHelpers.DangerousMakeTwosComplement(zd); // Mutates zd } - return new BigInteger(zd, negx); + result = new BigInteger(zd, negx); + + if (zdFromPool != null) + ArrayPool.Shared.Return(zdFromPool); + exit: + if (xdFromPool != null) + ArrayPool.Shared.Return(xdFromPool); + + return result; } public static BigInteger operator ~(BigInteger value) @@ -2443,34 +2472,26 @@ stackalloc uint[size] /// Encapsulate the logic of normalizing the "small" and "large" forms of BigInteger /// into the "large" form so that Bit Manipulation algorithms can be simplified. /// - /// /// /// The UInt32 array containing the entire big integer in "large" (denormalized) form. /// E.g., the number one (1) and negative one (-1) are both stored as 0x00000001 /// BigInteger values Int32.MinValue < x <= Int32.MaxValue are converted to this /// format for convenience. /// - /// The length of xd. /// True for negative numbers. - private static bool GetPartsForBitManipulation(ref BigInteger x, out uint[] xd, out int xl) + private bool GetPartsForBitManipulation(Span xd) { - if (x._bits == null) + Debug.Assert(_bits is null ? xd.Length == 1 : xd.Length == _bits.Length); + + if (_bits is null) { - if (x._sign < 0) - { - xd = new uint[] { (uint)-x._sign }; - } - else - { - xd = new uint[] { (uint)x._sign }; - } + xd[0] = (uint)(_sign < 0 ? -_sign : _sign); } else { - xd = x._bits; + _bits.CopyTo(xd); } - xl = (x._bits == null ? 1 : x._bits.Length); - return x._sign < 0; + return _sign < 0; } internal static int GetDiffLength(uint[] rgu1, uint[] rgu2, int cu) From ea350112a9a0a13059a317cc19b157ce1f6461b2 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 16:52:28 +0300 Subject: [PATCH 22/88] Array pooling for Square operation --- .../Numerics/BigIntegerCalculator.SquMul.cs | 52 ++++++++----------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index b6708d9ad8396..00ea11fde4ab2 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -94,42 +95,35 @@ private static void Square(ReadOnlySpan value, Span bits) Square(valueHigh, bitsHigh); int foldLength = valueHigh.Length + 1; + uint[]? foldFromPool = null; + Span fold = foldLength <= AllocationThreshold ? + stackalloc uint[foldLength] + : (foldFromPool = ArrayPool.Shared.Rent(foldLength)).AsSpan(0, foldLength); + fold.Clear(); + int coreLength = foldLength + foldLength; + uint[]? coreFromPool = null; + Span core = coreLength <= AllocationThreshold ? + stackalloc uint[coreLength] + : (coreFromPool = ArrayPool.Shared.Rent(coreLength)).AsSpan(0, coreLength); + core.Clear(); - Span result = bits.Slice(n); + // ... compute z_a = a_1 + a_0 (call it fold...) + Add(valueHigh, valueLow, fold); - if (coreLength < AllocationThreshold) - { - SquareFinal(valueHigh, valueLow, - ZeroMem(stackalloc uint[foldLength]), ZeroMem(stackalloc uint[coreLength]), - bitsHigh, bitsLow, - result); - } - else - { - SquareFinal(valueHigh, valueLow, - new uint[foldLength], new uint[coreLength], - bitsHigh, bitsLow, - result); - } + // ... compute z_1 = z_a * z_a - z_0 - z_2 + Square(fold, core); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static void SquareFinal(ReadOnlySpan valueHigh, ReadOnlySpan valueLow, - Span fold, Span core, - ReadOnlySpan bitsHigh, ReadOnlySpan bitsLow, - Span result) - { - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(valueHigh, valueLow, fold); + if (foldFromPool != null) + ArrayPool.Shared.Return(foldFromPool); - // ... compute z_1 = z_a * z_a - z_0 - z_2 - Square(fold, core); + SubtractCore(bitsHigh, bitsLow, core); - SubtractCore(bitsHigh, bitsLow, core); + // ... and finally merge the result! :-) + AddSelf(bits.Slice(n), core); - // ... and finally merge the result! :-) - AddSelf(result, core); - } + if (coreFromPool != null) + ArrayPool.Shared.Return(coreFromPool); } } From 9da2cb3a94372ca005e929231c54de790e0c720d Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 16:52:57 +0300 Subject: [PATCH 23/88] Fixed bound check for bitwise operations --- .../src/System/Numerics/BigInteger.cs | 23 ++++++++++--------- .../src/System/Numerics/NumericsHelpers.cs | 5 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 18754230671f3..af344364a7a1c 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -412,7 +412,7 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE if (isNegative) { - NumericsHelpers.DangerousMakeTwosComplement(val); // Mutates val + NumericsHelpers.MakeTwosComplement(val); // Mutates val // Pack _bits to remove any wasted space after the twos complement int len = val.Length - 1; @@ -559,7 +559,7 @@ private BigInteger(Span value) } // Finally handle the more complex cases where we must transform the input into sign magnitude - NumericsHelpers.DangerousMakeTwosComplement(value); // mutates val + NumericsHelpers.MakeTwosComplement(value); // mutates val // Pack _bits to remove any wasted space after the twos complement int len = value.Length; @@ -1440,16 +1440,17 @@ private int CopyTo(Span buffer) buffer[0] = unchecked((uint)_sign); highDWord = (_sign < 0) ? uint.MaxValue : 0; } - else if (_sign == -1) - { - _bits.CopyTo(buffer); - NumericsHelpers.DangerousMakeTwosComplement(buffer[..^1]); // Mutates dwords - highDWord = uint.MaxValue; - } else { _bits.CopyTo(buffer); - highDWord = 0; + buffer = buffer.Slice(0, _bits.Length + 1); + if (_sign == -1) + { + NumericsHelpers.MakeTwosComplement(buffer[..^1]); // Mutates dwords + highDWord = uint.MaxValue; + } + else + highDWord = 0; } // Find highest significant byte @@ -2072,7 +2073,7 @@ stackalloc uint[xl] result = MinusOne; goto exit; } - NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd + NumericsHelpers.MakeTwosComplement(xd); // Mutates xd } uint[]? zdFromPool = null; @@ -2106,7 +2107,7 @@ stackalloc uint[zl] } if (negx) { - NumericsHelpers.DangerousMakeTwosComplement(zd); // Mutates zd + NumericsHelpers.MakeTwosComplement(zd); // Mutates zd } result = new BigInteger(zd, negx); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs index cd26ceb8a5e59..db63f5043c67e 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs @@ -105,9 +105,8 @@ public static double GetDoubleFromParts(int sign, int exp, ulong man) return du.dbl; } - // Do an in-place two's complement. "Dangerous" because it causes - // a mutation and needs to be used with care for immutable types. - public static void DangerousMakeTwosComplement(Span d) + // Do an in-place two's complement. + public static void MakeTwosComplement(Span d) { if (d.Length > 0) { From ed6d159be91171edce5827b2e44cd8f4da3b4677 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 17:02:13 +0300 Subject: [PATCH 24/88] Multiply now uses array pooling --- .../Numerics/BigIntegerCalculator.SquMul.cs | 65 ++++++++++--------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 00ea11fde4ab2..ea92754691b87 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -236,45 +236,48 @@ private static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Multiply(leftHigh, rightHigh, bitsHigh); int leftFoldLength = leftHigh.Length + 1; + uint[]? leftFoldFromPool = null; + Span leftFold = leftFoldLength <= AllocationThreshold ? + stackalloc uint[leftFoldLength] + : (leftFoldFromPool = ArrayPool.Shared.Rent(leftFoldLength)).AsSpan(0, leftFoldLength); + leftFold.Clear(); + int rightFoldLength = rightHigh.Length + 1; + uint[]? rightFoldFromPool = null; + Span rightFold = rightFoldLength <= AllocationThreshold ? + stackalloc uint[rightFoldLength] + : (rightFoldFromPool = ArrayPool.Shared.Rent(rightFoldLength)).AsSpan(0, rightFoldLength); + rightFold.Clear(); + int coreLength = leftFoldLength + rightFoldLength; + uint[]? coreFromPool = null; + Span core = coreLength <= AllocationThreshold ? + stackalloc uint[coreLength] + : (coreFromPool = ArrayPool.Shared.Rent(coreLength)).AsSpan(0, coreLength); + core.Clear(); - Span result = bits.Slice(n); + // ... compute z_a = a_1 + a_0 (call it fold...) + Add(leftHigh, leftLow, leftFold); - if (coreLength < AllocationThreshold) - { - MultiplyFinal(leftHigh, leftLow, ZeroMem(stackalloc uint[leftFoldLength]), - rightHigh, rightLow, ZeroMem(stackalloc uint[rightFoldLength]), - bitsHigh, bitsLow, ZeroMem(stackalloc uint[coreLength]), - result); - } - else - { - MultiplyFinal(leftHigh, leftLow, new uint[leftFoldLength], - rightHigh, rightLow, new uint[rightFoldLength], - bitsHigh, bitsLow, new uint[coreLength], - result); - } + // ... compute z_b = b_1 + b_0 (call it fold...) + Add(rightHigh, rightLow, rightFold); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static void MultiplyFinal(ReadOnlySpan leftHigh, ReadOnlySpan leftLow, Span leftFold, - ReadOnlySpan rightHigh, ReadOnlySpan rightLow, Span rightFold, - ReadOnlySpan bitsHigh, ReadOnlySpan bitsLow, Span core, - Span result) - { - // ... compute z_a = a_1 + a_0 (call it fold...) - Add(leftHigh, leftLow, leftFold); + // ... compute z_1 = z_a * z_b - z_0 - z_2 + Multiply(leftFold, rightFold, core); - // ... compute z_b = b_1 + b_0 (call it fold...) - Add(rightHigh, rightLow, rightFold); + if (leftFoldFromPool != null) + ArrayPool.Shared.Return(leftFoldFromPool); - // ... compute z_1 = z_a * z_b - z_0 - z_2 - Multiply(leftFold, rightFold, core); - SubtractCore(bitsHigh, bitsLow, core); + if (rightFoldFromPool != null) + ArrayPool.Shared.Return(rightFoldFromPool); - // ... and finally merge the result! :-) - AddSelf(result, core); - } + SubtractCore(bitsHigh, bitsLow, core); + + // ... and finally merge the result! :-) + AddSelf(bits.Slice(n), core); + + if (coreFromPool != null) + ArrayPool.Shared.Return(coreFromPool); } } From e53dc14c7eec034d864e3a3b0f939c8e64173e9a Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 17:02:51 +0300 Subject: [PATCH 25/88] Removed namespace import --- .../src/System/Numerics/BigIntegerCalculator.SquMul.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index ea92754691b87..bc2a1ce2d799e 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Diagnostics; -using System.Runtime.CompilerServices; namespace System.Numerics { From 6d84785f4d703f866a94f3e576466d3c60f3f991 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 17:39:58 +0300 Subject: [PATCH 26/88] Reduced memory allocation for Add --- .../src/System/Numerics/BigInteger.cs | 69 ++++++++++++++----- .../Numerics/BigIntegerCalculator.AddSub.cs | 22 ++---- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index af344364a7a1c..06b9107ce1623 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1503,42 +1503,73 @@ public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan return BigNumber.TryFormatBigInteger(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } - private static BigInteger Add(uint[]? leftBits, int leftSign, uint[]? rightBits, int rightSign) + private static BigInteger Add(ReadOnlySpan leftBits, int leftSign, ReadOnlySpan rightBits, int rightSign) { - bool trivialLeft = leftBits == null; - bool trivialRight = rightBits == null; + bool trivialLeft = leftBits.IsEmpty; + bool trivialRight = rightBits.IsEmpty; if (trivialLeft && trivialRight) { return (long)leftSign + rightSign; } + BigInteger result; + uint[]? bitsFromPool = null; + if (trivialLeft) { - Debug.Assert(rightBits != null); - uint[] bits = BigIntegerCalculator.Add(rightBits, NumericsHelpers.Abs(leftSign)); - return new BigInteger(bits, leftSign < 0); - } + Debug.Assert(!rightBits.IsEmpty); - if (trivialRight) - { - Debug.Assert(leftBits != null); - uint[] bits = BigIntegerCalculator.Add(leftBits, NumericsHelpers.Abs(rightSign)); - return new BigInteger(bits, leftSign < 0); - } + int size = rightBits.Length + 1; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); - Debug.Assert(leftBits != null && rightBits != null); + BigIntegerCalculator.Add(rightBits, NumericsHelpers.Abs(leftSign), bits); - if (leftBits.Length < rightBits.Length) + result = new BigInteger(bits, leftSign < 0); + } + else if (trivialRight) { - uint[] bits = BigIntegerCalculator.Add(rightBits, leftBits); - return new BigInteger(bits, leftSign < 0); + Debug.Assert(!leftBits.IsEmpty); + + int size = leftBits.Length + 1; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Add(leftBits, NumericsHelpers.Abs(rightSign), bits); + result = new BigInteger(bits, leftSign < 0); } else { - uint[] bits = BigIntegerCalculator.Add(leftBits, rightBits); - return new BigInteger(bits, leftSign < 0); + Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); + + int size; + if (leftBits.Length < rightBits.Length) + { + size = rightBits.Length + 1; + ReadOnlySpan temp = leftBits; + leftBits = rightBits; + rightBits = temp; + } + else + { + size = leftBits.Length + 1; + } + + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Add(leftBits, rightBits, bits); + result = new BigInteger(bits, leftSign < 0); } + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static BigInteger operator -(BigInteger left, BigInteger right) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index aa2dc71b27bd2..d3e8b9397556c 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -8,40 +8,28 @@ namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Add(ReadOnlySpan left, uint right) + public static void Add(ReadOnlySpan left, uint right, Span result) { Debug.Assert(left.Length >= 1); + Debug.Assert(result.Length == left.Length + 1); // Executes the addition for one big and one 32-bit integer. // Thus, we've similar code than below, but there is no loop for // processing the 32-bit integer, since it's a single element. - uint[] bits = new uint[left.Length + 1]; long carry = right; for (int i = 0; i < left.Length; i++) { long digit = left[i] + carry; - bits[i] = unchecked((uint)digit); + result[i] = unchecked((uint)digit); carry = digit >> 32; } - bits[left.Length] = (uint)carry; - - return bits; - } - - public static uint[] Add(ReadOnlySpan left, ReadOnlySpan right) - { - Debug.Assert(left.Length >= right.Length); - - uint[] bits = new uint[left.Length + 1]; - Add(left, right, bits); - - return bits; + result[left.Length] = (uint)carry; } - private static void Add(ReadOnlySpan left, ReadOnlySpan right, Span bits) + public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span bits) { Debug.Assert(left.Length >= 0); Debug.Assert(right.Length >= 0); From a4482e0d28923c435bdadb00b66a9999b43d0c89 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 18:02:22 +0300 Subject: [PATCH 27/88] Removed swap of arguments --- .../src/System/Numerics/BigInteger.cs | 91 ++++++++++++------- .../Numerics/BigIntegerCalculator.AddSub.cs | 27 ++---- 2 files changed, 66 insertions(+), 52 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 06b9107ce1623..8026eaf429be8 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1526,7 +1526,6 @@ stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); BigIntegerCalculator.Add(rightBits, NumericsHelpers.Abs(leftSign), bits); - result = new BigInteger(bits, leftSign < 0); } else if (trivialRight) @@ -1541,23 +1540,23 @@ stackalloc uint[size] BigIntegerCalculator.Add(leftBits, NumericsHelpers.Abs(rightSign), bits); result = new BigInteger(bits, leftSign < 0); } - else + else if (leftBits.Length < rightBits.Length) { Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); - int size; - if (leftBits.Length < rightBits.Length) - { - size = rightBits.Length + 1; - ReadOnlySpan temp = leftBits; - leftBits = rightBits; - rightBits = temp; - } - else - { - size = leftBits.Length + 1; - } + int size = rightBits.Length + 1; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + BigIntegerCalculator.Add(rightBits, leftBits, bits); + result = new BigInteger(bits, leftSign < 0); + } + else + { + Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); + + int size = leftBits.Length + 1; Span bits = size <= StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1582,42 +1581,70 @@ stackalloc uint[size] return Subtract(left._bits, left._sign, right._bits, right._sign); } - private static BigInteger Subtract(uint[]? leftBits, int leftSign, uint[]? rightBits, int rightSign) + private static BigInteger Subtract(ReadOnlySpan leftBits, int leftSign, ReadOnlySpan rightBits, int rightSign) { - bool trivialLeft = leftBits == null; - bool trivialRight = rightBits == null; + bool trivialLeft = leftBits.IsEmpty; + bool trivialRight = rightBits.IsEmpty; if (trivialLeft && trivialRight) { return (long)leftSign - rightSign; } + BigInteger result; + uint[]? bitsFromPool = null; + if (trivialLeft) { - Debug.Assert(rightBits != null); - uint[] bits = BigIntegerCalculator.Subtract(rightBits, NumericsHelpers.Abs(leftSign)); - return new BigInteger(bits, leftSign >= 0); - } + Debug.Assert(!rightBits.IsEmpty); - if (trivialRight) - { - Debug.Assert(leftBits != null); - uint[] bits = BigIntegerCalculator.Subtract(leftBits, NumericsHelpers.Abs(rightSign)); - return new BigInteger(bits, leftSign < 0); + int size = rightBits.Length; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Subtract(rightBits, NumericsHelpers.Abs(leftSign), bits); + result = new BigInteger(bits, leftSign >= 0); } + else if (trivialRight) + { + Debug.Assert(!leftBits.IsEmpty); - Debug.Assert(leftBits != null && rightBits != null); + int size = leftBits.Length; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); - if (BigIntegerCalculator.Compare(leftBits, rightBits) < 0) + BigIntegerCalculator.Subtract(leftBits, NumericsHelpers.Abs(rightSign), bits); + result = new BigInteger(bits, leftSign < 0); + } + else if (BigIntegerCalculator.Compare(leftBits, rightBits) < 0) { - uint[] bits = BigIntegerCalculator.Subtract(rightBits, leftBits); - return new BigInteger(bits, leftSign >= 0); + int size = rightBits.Length; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Subtract(rightBits, leftBits, bits); + result = new BigInteger(bits, leftSign >= 0); } else { - uint[] bits = BigIntegerCalculator.Subtract(leftBits, rightBits); - return new BigInteger(bits, leftSign < 0); + Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); + + int size = leftBits.Length; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Subtract(leftBits, rightBits, bits); + result = new BigInteger(bits, leftSign < 0); } + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static implicit operator BigInteger(byte value) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index d3e8b9397556c..fd79c9ca4eb6b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -8,10 +8,10 @@ namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static void Add(ReadOnlySpan left, uint right, Span result) + public static void Add(ReadOnlySpan left, uint right, Span bits) { Debug.Assert(left.Length >= 1); - Debug.Assert(result.Length == left.Length + 1); + Debug.Assert(bits.Length == left.Length + 1); // Executes the addition for one big and one 32-bit integer. // Thus, we've similar code than below, but there is no loop for @@ -22,11 +22,11 @@ public static void Add(ReadOnlySpan left, uint right, Span result) for (int i = 0; i < left.Length; i++) { long digit = left[i] + carry; - result[i] = unchecked((uint)digit); + bits[i] = unchecked((uint)digit); carry = digit >> 32; } - result[left.Length] = (uint)carry; + bits[left.Length] = (uint)carry; } public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span bits) @@ -89,16 +89,16 @@ private static void AddSelf(Span left, ReadOnlySpan right) Debug.Assert(carry == 0); } - public static uint[] Subtract(ReadOnlySpan left, uint right) + public static void Subtract(ReadOnlySpan left, uint right, Span bits) { Debug.Assert(left.Length >= 1); Debug.Assert(left[0] >= right || left.Length >= 2); + Debug.Assert(bits.Length == left.Length); // Executes the subtraction for one big and one 32-bit integer. // Thus, we've similar code than below, but there is no loop for // processing the 32-bit integer, since it's a single element. - uint[] bits = new uint[left.Length]; long carry = -right; for (int i = 0; i < left.Length; i++) @@ -107,22 +107,9 @@ public static uint[] Subtract(ReadOnlySpan left, uint right) bits[i] = unchecked((uint)digit); carry = digit >> 32; } - - return bits; - } - - public static uint[] Subtract(ReadOnlySpan left, ReadOnlySpan right) - { - Debug.Assert(left.Length >= right.Length); - Debug.Assert(Compare(left, right) >= 0); - - uint[] bits = new uint[left.Length]; - Subtract(left, right, bits); - - return bits; } - private static void Subtract(ReadOnlySpan left, ReadOnlySpan right, Span bits) + public static void Subtract(ReadOnlySpan left, ReadOnlySpan right, Span bits) { Debug.Assert(left.Length >= 0); Debug.Assert(right.Length >= 0); From b0752eac8fdca7b9b53ba35cab3f28785fcca7fa Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 21:15:14 +0300 Subject: [PATCH 28/88] Reduced memory allocations for multiply operator --- .../src/System/Numerics/BigInteger.cs | 86 ++++++++++++++----- .../Numerics/BigIntegerCalculator.SquMul.cs | 29 ++----- 2 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 8026eaf429be8..f4559b21f75a6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.CompilerServices; namespace System.Numerics { @@ -2220,46 +2221,87 @@ stackalloc uint[zl] left.AssertValid(); right.AssertValid(); - bool trivialLeft = left._bits == null; - bool trivialRight = right._bits == null; + return Multiply(left._bits, left._sign, right._bits, right._sign); + } + + private static BigInteger Multiply(ReadOnlySpan left, int leftSign, ReadOnlySpan right, int rightSign) + { + bool trivialLeft = left.IsEmpty; + bool trivialRight = right.IsEmpty; if (trivialLeft && trivialRight) { - return (long)left._sign * right._sign; + return (long)leftSign * rightSign; } + BigInteger result; + uint[]? bitsFromPool = null; + if (trivialLeft) { - Debug.Assert(right._bits != null); - uint[] bits = BigIntegerCalculator.Multiply(right._bits, NumericsHelpers.Abs(left._sign)); - return new BigInteger(bits, (left._sign < 0) ^ (right._sign < 0)); - } + Debug.Assert(!right.IsEmpty); - if (trivialRight) - { - Debug.Assert(left._bits != null); - uint[] bits = BigIntegerCalculator.Multiply(left._bits, NumericsHelpers.Abs(right._sign)); - return new BigInteger(bits, (left._sign < 0) ^ (right._sign < 0)); + int size = right.Length + 1; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Multiply(right, NumericsHelpers.Abs(leftSign), bits); + result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } + else if (trivialRight) + { + Debug.Assert(!left.IsEmpty); - Debug.Assert(left._bits != null && right._bits != null); + int size = left.Length + 1; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); - if (left._bits == right._bits) - { - uint[] bits = BigIntegerCalculator.Square(left._bits); - return new BigInteger(bits, (left._sign < 0) ^ (right._sign < 0)); + BigIntegerCalculator.Multiply(left, NumericsHelpers.Abs(rightSign), bits); + result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } + else if (left.Length == right.Length && Unsafe.AreSame(ref Unsafe.AsRef(in left[0]), ref Unsafe.AsRef(in right[0]))) + { + int size = left.Length + right.Length; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); - if (left._bits.Length < right._bits.Length) + BigIntegerCalculator.Square(left, bits); + result = new BigInteger(bits, false); + } + else if (left.Length < right.Length) { - uint[] bits = BigIntegerCalculator.Multiply(right._bits, left._bits); - return new BigInteger(bits, (left._sign < 0) ^ (right._sign < 0)); + Debug.Assert(!left.IsEmpty && !right.IsEmpty); + + int size = left.Length + right.Length; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + bits.Clear(); + + BigIntegerCalculator.Multiply(right, left, bits); + result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } else { - uint[] bits = BigIntegerCalculator.Multiply(left._bits, right._bits); - return new BigInteger(bits, (left._sign < 0) ^ (right._sign < 0)); + Debug.Assert(!left.IsEmpty && !right.IsEmpty); + + int size = left.Length + right.Length; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + bits.Clear(); + + BigIntegerCalculator.Multiply(left, right, bits); + result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static BigInteger operator /(BigInteger dividend, BigInteger divisor) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index bc2a1ce2d799e..209fe3b72329d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -9,18 +9,10 @@ namespace System.Numerics { internal static partial class BigIntegerCalculator { - public static uint[] Square(ReadOnlySpan value) - { - uint[] bits = new uint[value.Length + value.Length]; - Square(value, bits); - - return bits; - } - // Mutable for unit testing... private static int SquareThreshold = 32; - private static void Square(ReadOnlySpan value, Span bits) + public static void Square(ReadOnlySpan value, Span bits) { Debug.Assert(value.Length >= 0); Debug.Assert(bits.Length == value.Length + value.Length); @@ -126,14 +118,15 @@ stackalloc uint[coreLength] } } - public static uint[] Multiply(ReadOnlySpan left, uint right) + public static void Multiply(ReadOnlySpan left, uint right, Span bits) { + Debug.Assert(bits.Length == left.Length + 1); + // Executes the multiplication for one big and one 32-bit integer. // Since every step holds the already slightly familiar equation // a_i * b + c <= 2^32 - 1 + (2^32 - 1)^2 < 2^64 - 1, // we are safe regarding to overflows. - uint[] bits = new uint[left.Length + 1]; int i = 0; ulong carry = 0UL; @@ -144,24 +137,12 @@ public static uint[] Multiply(ReadOnlySpan left, uint right) carry = digits >> 32; } bits[i] = (uint)carry; - - return bits; - } - - public static uint[] Multiply(ReadOnlySpan left, ReadOnlySpan right) - { - Debug.Assert(left.Length >= right.Length); - - uint[] bits = new uint[left.Length + right.Length]; - Multiply(left, right, bits); - - return bits; } // Mutable for unit testing... private static int MultiplyThreshold = 32; - private static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Span bits) + public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Span bits) { Debug.Assert(left.Length >= 0); Debug.Assert(right.Length >= 0); From 8c9cfdcef3595aadee1552d0cd636b7c87083b32 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 21:24:10 +0300 Subject: [PATCH 29/88] Reduced memory allocations for modulo operator --- .../src/System/Numerics/BigInteger.cs | 16 ++++++++++++++-- .../Numerics/BigIntegerCalculator.DivRem.cs | 16 ++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index f4559b21f75a6..3e6749a22d925 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -2404,8 +2404,20 @@ stackalloc uint[size] { return dividend; } - uint[] bits = BigIntegerCalculator.Remainder(dividend._bits, divisor._bits); - return new BigInteger(bits, dividend._sign < 0); + + uint[]? bitsFromPool = null; + int size = dividend._bits.Length; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + + BigIntegerCalculator.Remainder(dividend._bits, divisor._bits, bits); + var result = new BigInteger(bits, dividend._sign < 0); + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static bool operator <(BigInteger left, BigInteger right) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 50acfe4c1f653..8f7228ffd0a46 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -93,6 +93,9 @@ stackalloc uint[left.Length] left.CopyTo(leftCopy); Divide(leftCopy, right, quotient); + + if (leftCopyFromPool != null) + ArrayPool.Shared.Return(leftCopyFromPool); } public static uint[] Divide(ReadOnlySpan left, ReadOnlySpan right) @@ -135,6 +138,19 @@ public static uint[] Remainder(ReadOnlySpan left, ReadOnlySpan right return localLeft; } + public static void Remainder(ReadOnlySpan left, ReadOnlySpan right, Span remainder) + { + Debug.Assert(left.Length >= 1); + Debug.Assert(right.Length >= 1); + Debug.Assert(left.Length >= right.Length); + Debug.Assert(remainder.Length == left.Length); + + // Same as above, but only returning the remainder. + + left.CopyTo(remainder); + Divide(remainder, right, default); + } + private static void Divide(Span left, ReadOnlySpan right, Span bits) { Debug.Assert(left.Length >= 1); From 15b4eef6ae58ab6b5491bda46613f82d3660fcf1 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 30 Apr 2020 23:03:17 +0300 Subject: [PATCH 30/88] Temporarily spannify ActualLength method --- .../System/Numerics/BigIntegerCalculator.BitsBuffer.cs | 8 ++++---- .../Numerics/BigIntegerCalculator.FastReducer.cs | 6 +++--- .../src/System/Numerics/BigIntegerCalculator.PowMod.cs | 10 ++-------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index d648dc0db238a..e64d2c7559067 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -100,7 +100,7 @@ public void Reduce(uint[] modulus) { Divide(new Span(_bits, 0, _length), modulus, default); - _length = ActualLength(_bits, modulus.Length); + _length = ActualLength(new ReadOnlySpan(_bits, 0, modulus.Length)); } } @@ -113,7 +113,7 @@ public void Reduce(ref BitsBuffer modulus) { Divide(new Span(_bits, 0, _length), new Span(modulus._bits, 0, modulus._length), default); - _length = ActualLength(_bits, modulus._length); + _length = ActualLength(new ReadOnlySpan(_bits, 0, modulus._length)); } } @@ -174,7 +174,7 @@ public void Refresh(int maxLength) Array.Clear(_bits, maxLength, _length - maxLength); } - _length = ActualLength(_bits, maxLength); + _length = ActualLength(new ReadOnlySpan(_bits, 0, maxLength)); } private void Apply(ref BitsBuffer temp, int maxLength) @@ -191,7 +191,7 @@ private void Apply(ref BitsBuffer temp, int maxLength) temp._bits = _bits; _bits = t; - _length = ActualLength(_bits, maxLength); + _length = ActualLength(new ReadOnlySpan(_bits, 0, maxLength)); } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index f43b9ed8cb7a6..be5be5db926f9 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -101,7 +101,7 @@ private static int DivMul(uint[] left, int leftLength, new Span(bits, 0, leftLength + rightLength)); } - return ActualLength(bits, leftLength + rightLength); + return ActualLength(new ReadOnlySpan(bits, 0, leftLength + rightLength)); } return 0; @@ -127,12 +127,12 @@ private static int SubMod(uint[] left, int leftLength, rightLength = k; SubtractSelf(new Span(left, 0, leftLength), new ReadOnlySpan(right, 0, rightLength)); - leftLength = ActualLength(left, leftLength); + leftLength = ActualLength(new ReadOnlySpan(left, 0, leftLength)); while (Compare(new ReadOnlySpan(left, 0, leftLength), modulus) >= 0) { SubtractSelf(new Span(left, 0, leftLength), modulus); - leftLength = ActualLength(left, leftLength); + leftLength = ActualLength(new ReadOnlySpan(left, 0, leftLength)); } Array.Clear(left, leftLength, left.Length - leftLength); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index 6609609962340..a753a1b596a7a 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -386,18 +386,12 @@ private static void PowCore(uint power, ref FastReducer reducer, } } - private static int ActualLength(uint[] value) + private static int ActualLength(ReadOnlySpan value) { // Since we're reusing memory here, the actual length // of a given value may be less then the array's length - return ActualLength(value, value.Length); - } - - private static int ActualLength(uint[] value, int length) - { - Debug.Assert(value != null); - Debug.Assert(length <= value.Length); + int length = value.Length; while (length > 0 && value[length - 1] == 0) --length; From fb263bf06cba0523c391fa236da3e0150dcde742 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 00:07:33 +0300 Subject: [PATCH 31/88] Spannify FastReducer --- .../BigIntegerCalculator.BitsBuffer.cs | 4 +- .../BigIntegerCalculator.FastReducer.cs | 120 ++++++++---------- .../Numerics/BigIntegerCalculator.PowMod.cs | 99 +++++++++++++-- 3 files changed, 145 insertions(+), 78 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index e64d2c7559067..c5570ac7d5a7e 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -81,12 +81,12 @@ public void SquareSelf(ref BitsBuffer temp) Apply(ref temp, _length + _length); } - public void Reduce(ref FastReducer reducer) + public void Reduce(in FastReducer reducer) { // Executes a modulo operation using an optimized reducer. // Thus, no need of any switching here, happens in-line. - _length = reducer.Reduce(_bits, _length); + _length = reducer.Reduce(_bits.AsSpan(0, _length)); } public void Reduce(uint[] modulus) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index be5be5db926f9..4b230db2ad6ac 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Runtime.CompilerServices; namespace System.Numerics { @@ -15,129 +14,120 @@ internal static partial class BigIntegerCalculator // see https://en.wikipedia.org/wiki/Barrett_reduction - internal readonly struct FastReducer + internal readonly ref struct FastReducer { - private readonly uint[] _modulus; - private readonly uint[] _mu; - private readonly uint[] _q1; - private readonly uint[] _q2; + private readonly ReadOnlySpan _modulus; + private readonly ReadOnlySpan _mu; + private readonly Span _q1; + private readonly Span _q2; - private readonly int _muLength; - - public FastReducer(uint[] modulus) + public FastReducer(ReadOnlySpan modulus, Span r, Span mu, Span q1, Span q2) { - Debug.Assert(modulus != null); + Debug.Assert(!modulus.IsEmpty); + Debug.Assert(r.Length == modulus.Length * 2 + 1); + Debug.Assert(mu.Length == r.Length - modulus.Length + 1); + Debug.Assert(q1.Length == modulus.Length * 2 + 2); + Debug.Assert(q2.Length == modulus.Length * 2 + 2); // Let r = 4^k, with 2^k > m - uint[] r = new uint[modulus.Length * 2 + 1]; r[r.Length - 1] = 1; // Let mu = 4^k / m - _mu = Divide(r, modulus); + Divide(r, modulus, mu); _modulus = modulus; - // Allocate memory for quotients once - _q1 = new uint[modulus.Length * 2 + 2]; - _q2 = new uint[modulus.Length * 2 + 1]; + _q1 = q1; + _q2 = q2; - _muLength = ActualLength(_mu); + _mu = mu.Slice(0, ActualLength(mu)); } - public int Reduce(uint[] value, int length) + public int Reduce(Span value) { - Debug.Assert(value != null); - Debug.Assert(length <= value.Length); Debug.Assert(value.Length <= _modulus.Length * 2); // Trivial: value is shorter - if (length < _modulus.Length) - return length; + if (value.Length < _modulus.Length) + return value.Length; // Let q1 = v/2^(k-1) * mu - int l1 = DivMul(value, length, _mu, _muLength, - _q1, _modulus.Length - 1); + _q1.Clear(); + int l1 = DivMul(value, _mu, _q1, _modulus.Length - 1); // Let q2 = q1/2^(k+1) * m - int l2 = DivMul(_q1, l1, _modulus, _modulus.Length, - _q2, _modulus.Length + 1); + _q2.Clear(); + int l2 = DivMul(_q1.Slice(0, l1), _modulus, _q2, _modulus.Length + 1); // Let v = (v - q2) % 2^(k+1) - i*m - return SubMod(value, length, _q2, l2, - _modulus, _modulus.Length + 1); + return SubMod(value, _q2.Slice(0, l2), _modulus, _modulus.Length + 1); } - private static int DivMul(uint[] left, int leftLength, - uint[] right, int rightLength, - uint[] bits, int k) + private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Span bits, int k) { - Debug.Assert(left != null); - Debug.Assert(left.Length >= leftLength); - Debug.Assert(right != null); - Debug.Assert(right.Length >= rightLength); - Debug.Assert(bits != null); - Debug.Assert(bits.Length + k >= leftLength + rightLength); + Debug.Assert(!left.IsEmpty); + Debug.Assert(!right.IsEmpty); + Debug.Assert(!bits.IsEmpty); + Debug.Assert(bits.Length + k >= left.Length + right.Length); // Executes the multiplication algorithm for left and right, // but skips the first k limbs of left, which is equivalent to // preceding division by 2^(32*k). To spare memory allocations // we write the result to an already allocated memory. - Array.Clear(bits, 0, bits.Length); + bits.Clear(); - if (leftLength > k) + if (left.Length > k) { - leftLength -= k; + left = left.Slice(k); - if (leftLength < rightLength) + if (left.Length < right.Length) { - Multiply(new ReadOnlySpan(right, 0, rightLength), - new ReadOnlySpan(left, k, leftLength), - new Span(bits, 0, leftLength + rightLength)); + Multiply(right, + left, + bits.Slice(0, left.Length + right.Length)); } else { - Multiply(new ReadOnlySpan(left, k, leftLength), - new ReadOnlySpan(right, 0, rightLength), - new Span(bits, 0, leftLength + rightLength)); + Multiply(left, + right, + bits.Slice(0, left.Length + right.Length)); } - return ActualLength(new ReadOnlySpan(bits, 0, leftLength + rightLength)); + return ActualLength(bits.Slice(0, left.Length + right.Length)); } return 0; } - private static int SubMod(uint[] left, int leftLength, - uint[] right, int rightLength, - uint[] modulus, int k) + private static int SubMod(Span left, ReadOnlySpan right, ReadOnlySpan modulus, int k) { - Debug.Assert(left != null); - Debug.Assert(left.Length >= leftLength); - Debug.Assert(right != null); - Debug.Assert(right.Length >= rightLength); + Debug.Assert(!left.IsEmpty); + Debug.Assert(!right.IsEmpty); + + Span originalLeft = left; //TODO: Should be removed later // Executes the subtraction algorithm for left and right, // but considers only the first k limbs, which is equivalent to // preceding reduction by 2^(32*k). Furthermore, if left is // still greater than modulus, further subtractions are used. - if (leftLength > k) - leftLength = k; - if (rightLength > k) - rightLength = k; + if (left.Length > k) + left = left.Slice(0, k); + if (right.Length > k) + right = right.Slice(0, k); - SubtractSelf(new Span(left, 0, leftLength), new ReadOnlySpan(right, 0, rightLength)); - leftLength = ActualLength(new ReadOnlySpan(left, 0, leftLength)); + SubtractSelf(left, right); + left = left.Slice(0, ActualLength(left)); - while (Compare(new ReadOnlySpan(left, 0, leftLength), modulus) >= 0) + while (Compare(left, modulus) >= 0) { - SubtractSelf(new Span(left, 0, leftLength), modulus); - leftLength = ActualLength(new ReadOnlySpan(left, 0, leftLength)); + SubtractSelf(left, modulus); + left = left.Slice(0, ActualLength(left)); } - Array.Clear(left, leftLength, left.Length - leftLength); + originalLeft.Slice(left.Length, originalLeft.Length - left.Length).Clear(); //TODO: Should be removed later - return leftLength; + return left.Length; } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index a753a1b596a7a..f2d9b4789324d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Buffers; using System.Diagnostics; namespace System.Numerics @@ -244,8 +245,46 @@ private static uint[] PowCore(uint[] power, uint[] modulus, } else { - FastReducer reducer = new FastReducer(modulus); - PowCore(power, ref reducer, ref value, ref result, ref temp); + size = modulus.Length * 2 + 1; + uint[]? rFromPool = null; + Span r = size <= AllocationThreshold ? + stackalloc uint[size] + : (rFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + r.Clear(); + + size = r.Length - modulus.Length + 1; + uint[]? muFromPool = null; + Span mu = size <= AllocationThreshold ? + stackalloc uint[size] + : (muFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + mu.Clear(); + + size = modulus.Length * 2 + 2; + uint[]? q1FromPool = null; + Span q1 = size <= AllocationThreshold ? + stackalloc uint[size] + : (q1FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + q1.Clear(); + + uint[]? q2FromPool = null; + Span q2 = size <= AllocationThreshold ? + stackalloc uint[size] + : (q2FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + q2.Clear(); + + FastReducer reducer = new FastReducer(modulus, r, mu, q1, q2); + + if (rFromPool != null) + ArrayPool.Shared.Return(rFromPool); + + PowCore(power, reducer, ref value, ref result, ref temp); + + if (muFromPool != null) + ArrayPool.Shared.Return(muFromPool); + if (q1FromPool != null) + ArrayPool.Shared.Return(q1FromPool); + if (q2FromPool != null) + ArrayPool.Shared.Return(q2FromPool); } return result.GetBits(); @@ -267,8 +306,46 @@ private static uint[] PowCore(uint power, uint[] modulus, } else { - FastReducer reducer = new FastReducer(modulus); - PowCore(power, ref reducer, ref value, ref result, ref temp); + size = modulus.Length * 2 + 1; + uint[]? rFromPool = null; + Span r = size <= AllocationThreshold ? + stackalloc uint[size] + : (rFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + r.Clear(); + + size = r.Length - modulus.Length + 1; + uint[]? muFromPool = null; + Span mu = size <= AllocationThreshold ? + stackalloc uint[size] + : (muFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + mu.Clear(); + + size = modulus.Length * 2 + 2; + uint[]? q1FromPool = null; + Span q1 = size <= AllocationThreshold ? + stackalloc uint[size] + : (q1FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + q1.Clear(); + + uint[]? q2FromPool = null; + Span q2 = size <= AllocationThreshold ? + stackalloc uint[size] + : (q2FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + q2.Clear(); + + FastReducer reducer = new FastReducer(modulus, r, mu, q1, q2); + + if (rFromPool != null) + ArrayPool.Shared.Return(rFromPool); + + PowCore(power, reducer, ref value, ref result, ref temp); + + if (muFromPool != null) + ArrayPool.Shared.Return(muFromPool); + if (q1FromPool != null) + ArrayPool.Shared.Return(q1FromPool); + if (q2FromPool != null) + ArrayPool.Shared.Return(q2FromPool); } return result.GetBits(); @@ -330,7 +407,7 @@ private static void PowCore(uint power, uint[] modulus, } } - private static void PowCore(uint[] power, ref FastReducer reducer, + private static void PowCore(uint[] power, in FastReducer reducer, ref BitsBuffer value, ref BitsBuffer result, ref BitsBuffer temp) { @@ -348,19 +425,19 @@ private static void PowCore(uint[] power, ref FastReducer reducer, if ((p & 1) == 1) { result.MultiplySelf(ref value, ref temp); - result.Reduce(ref reducer); + result.Reduce(reducer); } value.SquareSelf(ref temp); - value.Reduce(ref reducer); + value.Reduce(reducer); p = p >> 1; } } - PowCore(power[power.Length - 1], ref reducer, ref value, ref result, + PowCore(power[power.Length - 1], reducer, ref value, ref result, ref temp); } - private static void PowCore(uint power, ref FastReducer reducer, + private static void PowCore(uint power, in FastReducer reducer, ref BitsBuffer value, ref BitsBuffer result, ref BitsBuffer temp) { @@ -375,12 +452,12 @@ private static void PowCore(uint power, ref FastReducer reducer, if ((power & 1) == 1) { result.MultiplySelf(ref value, ref temp); - result.Reduce(ref reducer); + result.Reduce(reducer); } if (power != 1) { value.SquareSelf(ref temp); - value.Reduce(ref reducer); + value.Reduce(reducer); } power = power >> 1; } From f71442d424e06625fbcc23b6f859a2cdc62d8e2b Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 11:00:50 +0300 Subject: [PATCH 32/88] Spannify initial parts of GCD alg --- .../Numerics/BigIntegerCalculator.GcdInv.cs | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index e62f55f6f7c3d..2891baff9d4b5 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -41,9 +41,8 @@ public static ulong Gcd(ulong left, ulong right) return left; } - public static uint Gcd(uint[] left, uint right) + public static uint Gcd(ReadOnlySpan left, uint right) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 1); Debug.Assert(right != 0); @@ -88,7 +87,7 @@ private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) { ulong x, y; - ExtractDigits(ref left, ref right, out x, out y); + ExtractDigits(new ReadOnlySpan(left.GetBits(), 0, left.GetLength()), new ReadOnlySpan(right.GetBits(), 0, right.GetLength()), out x, out y); uint a = 1U, b = 0U; uint c = 0U, d = 1U; @@ -187,48 +186,42 @@ private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) } } - private static void ExtractDigits(ref BitsBuffer xBuffer, - ref BitsBuffer yBuffer, + private static void ExtractDigits(ReadOnlySpan xBuffer, + ReadOnlySpan yBuffer, out ulong x, out ulong y) { - Debug.Assert(xBuffer.GetLength() >= 3); - Debug.Assert(yBuffer.GetLength() >= 3); - Debug.Assert(xBuffer.GetLength() >= yBuffer.GetLength()); + Debug.Assert(xBuffer.Length >= 3); + Debug.Assert(yBuffer.Length >= 3); + Debug.Assert(xBuffer.Length >= yBuffer.Length); // Extracts the most significant bits of x and y, // but ensures the quotient x / y does not change! - uint[] xBits = xBuffer.GetBits(); - int xLength = xBuffer.GetLength(); - - uint[] yBits = yBuffer.GetBits(); - int yLength = yBuffer.GetLength(); - - ulong xh = xBits[xLength - 1]; - ulong xm = xBits[xLength - 2]; - ulong xl = xBits[xLength - 3]; + ulong xh = xBuffer[xBuffer.Length - 1]; + ulong xm = xBuffer[xBuffer.Length - 2]; + ulong xl = xBuffer[xBuffer.Length - 3]; ulong yh, ym, yl; // arrange the bits - switch (xLength - yLength) + switch (xBuffer.Length - yBuffer.Length) { case 0: - yh = yBits[yLength - 1]; - ym = yBits[yLength - 2]; - yl = yBits[yLength - 3]; + yh = yBuffer[yBuffer.Length - 1]; + ym = yBuffer[yBuffer.Length - 2]; + yl = yBuffer[yBuffer.Length - 3]; break; case 1: yh = 0UL; - ym = yBits[yLength - 1]; - yl = yBits[yLength - 2]; + ym = yBuffer[yBuffer.Length - 1]; + yl = yBuffer[yBuffer.Length - 2]; break; case 2: yh = 0UL; ym = 0UL; - yl = yBits[yLength - 1]; + yl = yBuffer[yBuffer.Length - 1]; break; default: From 156b7ac2a0192258e7f916b0c8f3431924954ff7 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 12:58:02 +0300 Subject: [PATCH 33/88] Simple GCD test for debugging --- .../tests/BigInteger/gcd.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs index a100dc64de334..a6e04d9cb36fa 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs @@ -66,6 +66,20 @@ public static void SpecialRegressionTests() VerifyGCDString(Print(tempByteArray1) + Print(tempByteArray2) + "bGCD"); } + [Fact] + public static void SimpleGCDTest() + { + var tempByteArray1 = new byte[] { 255, 255, 255, 255, 255, 255, 255, 255, 1 }; + var tempByteArray2 = new byte[] { 255, 255, 255, 255, 255, 255, 255, 255, 1 }; + + var result = MyBigIntImp.GCD(new Collections.Generic.List(tempByteArray1), new Collections.Generic.List(tempByteArray2)).ToArray(); + Console.WriteLine(Print(result)); + + result = BigInteger.GreatestCommonDivisor(new BigInteger(tempByteArray1), new BigInteger(tempByteArray2)).ToByteArray(); + + Console.WriteLine(Print(result)); + } + [Fact] public static void RunGCDTests() { From 036c535a5eec298176ab8fc33b89c12d7e108998 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 13:27:02 +0300 Subject: [PATCH 34/88] Simple test for debugging --- .../System.Runtime.Numerics/tests/BigInteger/gcd.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs index a6e04d9cb36fa..c8d96191b342e 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs @@ -71,10 +71,12 @@ public static void SimpleGCDTest() { var tempByteArray1 = new byte[] { 255, 255, 255, 255, 255, 255, 255, 255, 1 }; var tempByteArray2 = new byte[] { 255, 255, 255, 255, 255, 255, 255, 255, 1 }; - + + Console.WriteLine("Correct GCD"); var result = MyBigIntImp.GCD(new Collections.Generic.List(tempByteArray1), new Collections.Generic.List(tempByteArray2)).ToArray(); Console.WriteLine(Print(result)); - + + Console.WriteLine("Incorrect GCD"); result = BigInteger.GreatestCommonDivisor(new BigInteger(tempByteArray1), new BigInteger(tempByteArray2)).ToByteArray(); Console.WriteLine(Print(result)); From 7fa1438a71e5a12a6c291e018c13a65737cad0d9 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 14:25:02 +0300 Subject: [PATCH 35/88] Removed debug code --- .../tests/BigInteger/gcd.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs index c8d96191b342e..a100dc64de334 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/gcd.cs @@ -66,22 +66,6 @@ public static void SpecialRegressionTests() VerifyGCDString(Print(tempByteArray1) + Print(tempByteArray2) + "bGCD"); } - [Fact] - public static void SimpleGCDTest() - { - var tempByteArray1 = new byte[] { 255, 255, 255, 255, 255, 255, 255, 255, 1 }; - var tempByteArray2 = new byte[] { 255, 255, 255, 255, 255, 255, 255, 255, 1 }; - - Console.WriteLine("Correct GCD"); - var result = MyBigIntImp.GCD(new Collections.Generic.List(tempByteArray1), new Collections.Generic.List(tempByteArray2)).ToArray(); - Console.WriteLine(Print(result)); - - Console.WriteLine("Incorrect GCD"); - result = BigInteger.GreatestCommonDivisor(new BigInteger(tempByteArray1), new BigInteger(tempByteArray2)).ToByteArray(); - - Console.WriteLine(Print(result)); - } - [Fact] public static void RunGCDTests() { From a4c489181d9534b1fb27eefbf69eee4ea8ddaa54 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 14:31:53 +0300 Subject: [PATCH 36/88] Spannified GCD algorithm --- .../src/System/Numerics/BigInteger.cs | 16 ++- .../Numerics/BigIntegerCalculator.GcdInv.cs | 136 +++++++++++++----- 2 files changed, 114 insertions(+), 38 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 3e6749a22d925..e495fccbabe2b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -881,7 +881,7 @@ public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right } } - private static BigInteger GreatestCommonDivisor(uint[] leftBits, uint[] rightBits) + private static BigInteger GreatestCommonDivisor(ReadOnlySpan leftBits, ReadOnlySpan rightBits) { Debug.Assert(BigIntegerCalculator.Compare(leftBits, rightBits) >= 0); @@ -902,8 +902,18 @@ private static BigInteger GreatestCommonDivisor(uint[] leftBits, uint[] rightBit return BigIntegerCalculator.Gcd(left, right); } - uint[] bits = BigIntegerCalculator.Gcd(leftBits, rightBits); - return new BigInteger(bits, false); + uint[]? bitsFromPool = null; + Span bits = leftBits.Length <= StackAllocThreshold ? + stackalloc uint[leftBits.Length] + : (bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).AsSpan(0, leftBits.Length); + + BigIntegerCalculator.Gcd(leftBits, rightBits, bits); + var result = new BigInteger(bits, false); + + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); + + return result; } public static BigInteger Max(BigInteger left, BigInteger right) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 2891baff9d4b5..d23a423ad9d62 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Buffers; using System.Diagnostics; namespace System.Numerics @@ -54,27 +55,32 @@ public static uint Gcd(ReadOnlySpan left, uint right) return Gcd(right, temp); } - public static uint[] Gcd(uint[] left, uint[] right) + public static void Gcd(ReadOnlySpan left, ReadOnlySpan right, Span result) { - Debug.Assert(left != null); Debug.Assert(left.Length >= 2); - Debug.Assert(right != null); Debug.Assert(right.Length >= 2); Debug.Assert(Compare(left, right) >= 0); + Debug.Assert(result.Length == left.Length); - BitsBuffer leftBuffer = new BitsBuffer(left.Length, left); - BitsBuffer rightBuffer = new BitsBuffer(right.Length, right); + left.CopyTo(result); - Gcd(ref leftBuffer, ref rightBuffer); + uint[]? rightCopyFromPool = null; + Span rightCopy = right.Length <= AllocationThreshold ? + stackalloc uint[right.Length] + : (rightCopyFromPool = ArrayPool.Shared.Rent(right.Length)).AsSpan(0, right.Length); + right.CopyTo(rightCopy); - return leftBuffer.GetBits(); + Gcd(result, rightCopy).CopyTo(result); + + if (rightCopyFromPool != null) + ArrayPool.Shared.Return(rightCopyFromPool); } - private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) + private static Span Gcd(Span left, Span right) { - Debug.Assert(left.GetLength() >= 2); - Debug.Assert(right.GetLength() >= 2); - Debug.Assert(left.GetLength() >= right.GetLength()); + Debug.Assert(left.Length >= 2); + Debug.Assert(right.Length >= 2); + Debug.Assert(left.Length >= right.Length); // Executes Lehmer's gcd algorithm, but uses the most // significant bits to work with 64-bit (not 32-bit) values. @@ -83,11 +89,11 @@ private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) // http://cacr.uwaterloo.ca/hac/about/chap14.pdf (see 14.4.2) // ftp://ftp.risc.uni-linz.ac.at/pub/techreports/1992/92-69.ps.gz - while (right.GetLength() > 2) + while (right.Length > 2) { ulong x, y; - ExtractDigits(new ReadOnlySpan(left.GetBits(), 0, left.GetLength()), new ReadOnlySpan(right.GetBits(), 0, right.GetLength()), out x, out y); + ExtractDigits(left, right, out x, out y); uint a = 1U, b = 0U; uint c = 0U, d = 1U; @@ -149,9 +155,9 @@ private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) if (b == 0) { // Euclid's step - left.Reduce(ref right); + left = left.Slice(0, Reduce(left, right)); - BitsBuffer temp = left; + Span temp = left; left = right; right = temp; } @@ -163,27 +169,77 @@ private static void Gcd(ref BitsBuffer left, ref BitsBuffer right) if (iteration % 2 == 1) { // Ensure left is larger than right - BitsBuffer temp = left; + Span temp = left; left = right; right = temp; } } } - if (right.GetLength() > 0) + if (right.Length > 0) { // Euclid's step - left.Reduce(ref right); + Reduce(left, right); - uint[] xBits = right.GetBits(); - uint[] yBits = left.GetBits(); + ulong x = ((ulong)right[1] << 32) | right[0]; + ulong y = ((ulong)left[1] << 32) | left[0]; + + Overwrite(ref left, Gcd(x, y)); + Overwrite(ref right, 0U); + } - ulong x = ((ulong)xBits[1] << 32) | xBits[0]; - ulong y = ((ulong)yBits[1] << 32) | yBits[0]; + return left; + } + + private static int Reduce(Span bits, ReadOnlySpan modulus) + { + // Executes a modulo operation using the divide operation. + // Thus, no need of any switching here, happens in-line. - left.Overwrite(Gcd(x, y)); - right.Overwrite(0); + if (bits.Length >= modulus.Length) + { + Divide(bits, modulus, default); + + return ActualLength(bits.Slice(0, modulus.Length)); } + return bits.Length; + } + + private static void Overwrite(ref Span buffer, ulong value) + { + Debug.Assert(buffer.Length >= 2); + + if (buffer.Length > 2) + { + // Ensure leading zeros + buffer.Slice(2).Clear(); + } + + uint lo = unchecked((uint)value); + uint hi = (uint)(value >> 32); + + buffer[0] = lo; + buffer[1] = hi; + if (hi != 0) + buffer = buffer.Slice(0, 2); + else if (lo != 0) + buffer = buffer.Slice(0, 1); + else + buffer = default; + } + + private static void Overwrite(ref Span bits, uint value) + { + Debug.Assert(bits.Length >= 1); + + if (bits.Length > 1) + { + // Ensure leading zeros + bits.Slice(1).Clear(); + } + + bits[0] = value; + bits = value != 0 ? bits.Slice(0, 1) : default; } private static void ExtractDigits(ReadOnlySpan xBuffer, @@ -240,23 +296,20 @@ private static void ExtractDigits(ReadOnlySpan xBuffer, Debug.Assert(x >= y); } - private static void LehmerCore(ref BitsBuffer xBuffer, - ref BitsBuffer yBuffer, + private static void LehmerCore(ref Span x, + ref Span y, long a, long b, long c, long d) { - Debug.Assert(xBuffer.GetLength() >= 1); - Debug.Assert(yBuffer.GetLength() >= 1); - Debug.Assert(xBuffer.GetLength() >= yBuffer.GetLength()); + Debug.Assert(x.Length >= 1); + Debug.Assert(y.Length >= 1); + Debug.Assert(x.Length >= y.Length); Debug.Assert(a <= 0x7FFFFFFF && b <= 0x7FFFFFFF); Debug.Assert(c <= 0x7FFFFFFF && d <= 0x7FFFFFFF); // Executes the combined calculation of Lehmer's step. - uint[] x = xBuffer.GetBits(); - uint[] y = yBuffer.GetBits(); - - int length = yBuffer.GetLength(); + int length = y.Length; long xCarry = 0L, yCarry = 0L; for (int i = 0; i < length; i++) @@ -269,8 +322,21 @@ private static void LehmerCore(ref BitsBuffer xBuffer, y[i] = unchecked((uint)yDigit); } - xBuffer.Refresh(length); - yBuffer.Refresh(length); + Refresh(ref x, length); + Refresh(ref y, length); + } + + private static void Refresh(ref Span bits, int maxLength) + { + Debug.Assert(bits.Length >= maxLength); + + if (bits.Length > maxLength) + { + // Ensure leading zeros + bits.Slice(maxLength).Clear(); + } + + bits = bits.Slice(0, ActualLength(bits.Slice(0, maxLength))); } } } From 900ac4843e493ff60f02494ce7d4e2a4e38585e8 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 14:35:54 +0300 Subject: [PATCH 37/88] Removed arg passing by ref --- .../Numerics/BigIntegerCalculator.GcdInv.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index d23a423ad9d62..6a54c782647cf 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -184,8 +184,8 @@ private static Span Gcd(Span left, Span right) ulong x = ((ulong)right[1] << 32) | right[0]; ulong y = ((ulong)left[1] << 32) | left[0]; - Overwrite(ref left, Gcd(x, y)); - Overwrite(ref right, 0U); + left = left.Slice(0, Overwrite(left, Gcd(x, y))); + right = right.Slice(0, Overwrite(right, 0U)); } return left; @@ -205,7 +205,7 @@ private static int Reduce(Span bits, ReadOnlySpan modulus) return bits.Length; } - private static void Overwrite(ref Span buffer, ulong value) + private static int Overwrite(Span buffer, ulong value) { Debug.Assert(buffer.Length >= 2); @@ -220,15 +220,10 @@ private static void Overwrite(ref Span buffer, ulong value) buffer[0] = lo; buffer[1] = hi; - if (hi != 0) - buffer = buffer.Slice(0, 2); - else if (lo != 0) - buffer = buffer.Slice(0, 1); - else - buffer = default; + return hi != 0 ? 2 : lo != 0 ? 1 : 0; } - private static void Overwrite(ref Span bits, uint value) + private static int Overwrite(Span bits, uint value) { Debug.Assert(bits.Length >= 1); @@ -239,7 +234,7 @@ private static void Overwrite(ref Span bits, uint value) } bits[0] = value; - bits = value != 0 ? bits.Slice(0, 1) : default; + return value != 0 ? 1 : 0; } private static void ExtractDigits(ReadOnlySpan xBuffer, From 3a5a117601e95ccb4b1543082f0066f2f925bda4 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 14:54:36 +0300 Subject: [PATCH 38/88] Complete migration of gcd to span --- .../Numerics/BigIntegerCalculator.GcdInv.cs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 6a54c782647cf..dd162fa942c73 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -70,18 +70,20 @@ stackalloc uint[right.Length] : (rightCopyFromPool = ArrayPool.Shared.Rent(right.Length)).AsSpan(0, right.Length); right.CopyTo(rightCopy); - Gcd(result, rightCopy).CopyTo(result); + Gcd(result, rightCopy); if (rightCopyFromPool != null) ArrayPool.Shared.Return(rightCopyFromPool); } - private static Span Gcd(Span left, Span right) + private static void Gcd(Span left, Span right) { Debug.Assert(left.Length >= 2); Debug.Assert(right.Length >= 2); Debug.Assert(left.Length >= right.Length); + Span result = left; //keep result buffer untouched during computation + // Executes Lehmer's gcd algorithm, but uses the most // significant bits to work with 64-bit (not 32-bit) values. // Furthermore we're using an optimized version due to Jebelean. @@ -164,7 +166,9 @@ private static Span Gcd(Span left, Span right) else { // Lehmer's step - LehmerCore(ref left, ref right, a, b, c, d); + var count = LehmerCore(left, right, a, b, c, d); + left = left.Slice(0, Refresh(left, count)); + right = right.Slice(0, Refresh(right, count)); if (iteration % 2 == 1) { @@ -185,10 +189,10 @@ private static Span Gcd(Span left, Span right) ulong y = ((ulong)left[1] << 32) | left[0]; left = left.Slice(0, Overwrite(left, Gcd(x, y))); - right = right.Slice(0, Overwrite(right, 0U)); + Overwrite(right, 0U); } - return left; + left.CopyTo(result); } private static int Reduce(Span bits, ReadOnlySpan modulus) @@ -291,10 +295,10 @@ private static void ExtractDigits(ReadOnlySpan xBuffer, Debug.Assert(x >= y); } - private static void LehmerCore(ref Span x, - ref Span y, - long a, long b, - long c, long d) + private static int LehmerCore(Span x, + Span y, + long a, long b, + long c, long d) { Debug.Assert(x.Length >= 1); Debug.Assert(y.Length >= 1); @@ -317,11 +321,10 @@ private static void LehmerCore(ref Span x, y[i] = unchecked((uint)yDigit); } - Refresh(ref x, length); - Refresh(ref y, length); + return length; } - private static void Refresh(ref Span bits, int maxLength) + private static int Refresh(Span bits, int maxLength) { Debug.Assert(bits.Length >= maxLength); @@ -331,7 +334,7 @@ private static void Refresh(ref Span bits, int maxLength) bits.Slice(maxLength).Clear(); } - bits = bits.Slice(0, ActualLength(bits.Slice(0, maxLength))); + return ActualLength(bits.Slice(0, maxLength)); } } } From 97ee31bc98167185393937e1fa4c21f374d1b9d6 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 15:06:47 +0300 Subject: [PATCH 39/88] Removed memory allocation for trivial case of GCD --- .../src/System/Numerics/BigInteger.cs | 29 +++++++----- .../BigIntegerCalculator.BitsBuffer.cs | 45 ------------------- .../Numerics/BigIntegerCalculator.DivRem.cs | 23 ---------- 3 files changed, 18 insertions(+), 79 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index e495fccbabe2b..4cfd14c15409e 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -885,30 +885,37 @@ private static BigInteger GreatestCommonDivisor(ReadOnlySpan leftBits, Rea { Debug.Assert(BigIntegerCalculator.Compare(leftBits, rightBits) >= 0); + uint[]? bitsFromPool = null; + BigInteger result; + // Short circuits to spare some allocations... if (rightBits.Length == 1) { uint temp = BigIntegerCalculator.Remainder(leftBits, rightBits[0]); - return BigIntegerCalculator.Gcd(rightBits[0], temp); + result = BigIntegerCalculator.Gcd(rightBits[0], temp); } - - if (rightBits.Length == 2) + else if (rightBits.Length == 2) { - uint[] tempBits = BigIntegerCalculator.Remainder(leftBits, rightBits); + Span bits = leftBits.Length <= StackAllocThreshold ? + stackalloc uint[leftBits.Length] + : (bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).AsSpan(0, leftBits.Length); + + BigIntegerCalculator.Remainder(leftBits, rightBits, bits); ulong left = ((ulong)rightBits[1] << 32) | rightBits[0]; - ulong right = ((ulong)tempBits[1] << 32) | tempBits[0]; + ulong right = ((ulong)bits[1] << 32) | bits[0]; - return BigIntegerCalculator.Gcd(left, right); + result = BigIntegerCalculator.Gcd(left, right); } - - uint[]? bitsFromPool = null; - Span bits = leftBits.Length <= StackAllocThreshold ? + else + { + Span bits = leftBits.Length <= StackAllocThreshold ? stackalloc uint[leftBits.Length] : (bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).AsSpan(0, leftBits.Length); - BigIntegerCalculator.Gcd(leftBits, rightBits, bits); - var result = new BigInteger(bits, false); + BigIntegerCalculator.Gcd(leftBits, rightBits, bits); + result = new BigInteger(bits, false); + } if (bitsFromPool != null) ArrayPool.Shared.Return(bitsFromPool); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs index c5570ac7d5a7e..bc9eb3d7bec93 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -104,51 +104,6 @@ public void Reduce(uint[] modulus) } } - public void Reduce(ref BitsBuffer modulus) - { - // Executes a modulo operation using the divide operation. - // Thus, no need of any switching here, happens in-line. - - if (_length >= modulus._length) - { - Divide(new Span(_bits, 0, _length), new Span(modulus._bits, 0, modulus._length), default); - - _length = ActualLength(new ReadOnlySpan(_bits, 0, modulus._length)); - } - } - - public void Overwrite(ulong value) - { - Debug.Assert(_bits.Length >= 2); - - if (_length > 2) - { - // Ensure leading zeros - Array.Clear(_bits, 2, _length - 2); - } - - uint lo = unchecked((uint)value); - uint hi = (uint)(value >> 32); - - _bits[0] = lo; - _bits[1] = hi; - _length = hi != 0 ? 2 : lo != 0 ? 1 : 0; - } - - public void Overwrite(uint value) - { - Debug.Assert(_bits.Length >= 1); - - if (_length > 1) - { - // Ensure leading zeros - Array.Clear(_bits, 1, _length - 1); - } - - _bits[0] = value; - _length = value != 0 ? 1 : 0; - } - public uint[] GetBits() { return _bits; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 8f7228ffd0a46..25c5203e3d73b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -98,29 +98,6 @@ stackalloc uint[left.Length] ArrayPool.Shared.Return(leftCopyFromPool); } - public static uint[] Divide(ReadOnlySpan left, ReadOnlySpan right) - { - Debug.Assert(left.Length >= 1); - Debug.Assert(right.Length >= 1); - Debug.Assert(left.Length >= right.Length); - - // Same as above, but only returning the quotient. - - uint[] bits = new uint[left.Length - right.Length + 1]; - uint[]? leftCopyFromPool = null; - - // NOTE: left will get overwritten, we need a local copy - // However, mutated left is not used afterwards, so use array pooling or stack alloc - Span leftCopy = left.Length < AllocationThreshold ? - stackalloc uint[left.Length] - : (leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).AsSpan(0, left.Length); - left.CopyTo(leftCopy); - - Divide(leftCopy, right, bits); - - return bits; - } - public static uint[] Remainder(ReadOnlySpan left, ReadOnlySpan right) { Debug.Assert(left.Length >= 1); From 4e6a40366b207429c544f68d99f7dfa410a2a713 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 15:14:14 +0300 Subject: [PATCH 40/88] Reorganize utility code --- .../Numerics/BigIntegerCalculator.PowMod.cs | 12 ----------- .../Numerics/BigIntegerCalculator.Utils.cs | 21 +++++++++++-------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index f2d9b4789324d..eaed4675eb473 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -462,17 +462,5 @@ private static void PowCore(uint power, in FastReducer reducer, power = power >> 1; } } - - private static int ActualLength(ReadOnlySpan value) - { - // Since we're reusing memory here, the actual length - // of a given value may be less then the array's length - - int length = value.Length; - - while (length > 0 && value[length - 1] == 0) - --length; - return length; - } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs index 7034aa0ad82da..3b62505ad3613 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.CompilerServices; - namespace System.Numerics { internal static partial class BigIntegerCalculator @@ -11,13 +9,6 @@ internal static partial class BigIntegerCalculator // Mutable for unit testing... private static int AllocationThreshold = 256; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Span ZeroMem(Span memory) - { - memory.Clear(); - return memory; - } - public static int Compare(ReadOnlySpan left, ReadOnlySpan right) { if (left.Length < right.Length) @@ -37,5 +28,17 @@ public static int Compare(ReadOnlySpan left, ReadOnlySpan right) return 0; } + + private static int ActualLength(ReadOnlySpan value) + { + // Since we're reusing memory here, the actual length + // of a given value may be less then the array's length + + int length = value.Length; + + while (length > 0 && value[length - 1] == 0) + --length; + return length; + } } } \ No newline at end of file From dd69148055448daf9508345a906a8ad890b377c0 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 1 May 2020 16:58:41 +0300 Subject: [PATCH 41/88] Removed redundant parameter --- .../src/System/Numerics/BigIntegerCalculator.PowMod.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index eaed4675eb473..e691e05348690 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -20,7 +20,7 @@ public static uint[] Pow(uint value, uint power) // To spare memory allocations we first roughly // estimate an upper bound for our buffers. - int size = PowBound(power, 1, 1); + int size = PowBound(power, 1); BitsBuffer v = new BitsBuffer(size, value); return PowCore(power, ref v); } @@ -33,7 +33,7 @@ public static uint[] Pow(uint[] value, uint power) // To spare memory allocations we first roughly // estimate an upper bound for our buffers. - int size = PowBound(power, value.Length, 1); + int size = PowBound(power, value.Length); BitsBuffer v = new BitsBuffer(size, value); return PowCore(power, ref v); } @@ -52,12 +52,12 @@ private static uint[] PowCore(uint power, ref BitsBuffer value) return result.GetBits(); } - private static int PowBound(uint power, int valueLength, - int resultLength) + private static int PowBound(uint power, int valueLength) { // The basic pow algorithm, but instead of squaring // and multiplying we just sum up the lengths. + int resultLength = 1; while (power != 0) { checked From 1f84dc77295a0ffec8816927d8d0119215f12383 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 12:49:39 +0300 Subject: [PATCH 42/88] Reduced memory alloc for trivial case of Pow alg --- .../src/System/Numerics/BigInteger.cs | 31 ++++- .../Numerics/BigIntegerCalculator.PowMod.cs | 120 ++++++++++++------ 2 files changed, 109 insertions(+), 42 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 4cfd14c15409e..2310aade6bdb9 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -984,6 +984,10 @@ public static BigInteger Pow(BigInteger value, int exponent) bool trivialValue = value._bits == null; + uint power = NumericsHelpers.Abs(exponent); + uint[]? valuesFromPool = null; + BigInteger result; + if (trivialValue) { if (value._sign == 1) @@ -992,13 +996,32 @@ public static BigInteger Pow(BigInteger value, int exponent) return (exponent & 1) != 0 ? value : s_bnOneInt; if (value._sign == 0) return value; + + int size = BigIntegerCalculator.PowBound(power, 1); + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (valuesFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + bits.Clear(); + + BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), power, bits); + result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0); } + else + { + int size = BigIntegerCalculator.PowBound(power, value._bits!.Length); + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (valuesFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + bits.Clear(); - uint[] bits = trivialValue - ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent)) - : BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent)); + BigIntegerCalculator.Pow(value._bits, power, bits); + result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0); + } + + if (valuesFromPool != null) + ArrayPool.Shared.Return(valuesFromPool); - return new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0); + return result; } public override int GetHashCode() diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index e691e05348690..4a08b0812891d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Diagnostics; +using System.Runtime.InteropServices; namespace System.Numerics { @@ -14,45 +15,103 @@ internal static partial class BigIntegerCalculator // https://en.wikipedia.org/wiki/Exponentiation_by_squaring - public static uint[] Pow(uint value, uint power) + public static void Pow(uint value, uint power, Span bits) { - // The basic pow method for a 32-bit integer. - // To spare memory allocations we first roughly - // estimate an upper bound for our buffers. - - int size = PowBound(power, 1); - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, ref v); + Pow(value != 0U ? MemoryMarshal.CreateReadOnlySpan(ref value, 1) : default, power, bits); } - public static uint[] Pow(uint[] value, uint power) + public static void Pow(ReadOnlySpan value, uint power, Span bits) { - Debug.Assert(value != null); + Debug.Assert(bits.Length == PowBound(power, value.Length)); // The basic pow method for a big integer. - // To spare memory allocations we first roughly - // estimate an upper bound for our buffers. + uint[]? tempFromPool = null; + Span temp = bits.Length <= AllocationThreshold ? + stackalloc uint[bits.Length] + : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); + temp.Clear(); + + uint[]? valueCopyFromPool = null; + Span valueCopy = bits.Length <= AllocationThreshold ? + stackalloc uint[bits.Length] + : (valueCopyFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); + valueCopy.Clear(); + value.CopyTo(valueCopy); + + PowCore(valueCopy, value.Length, temp, power, bits); + + if (tempFromPool != null) + ArrayPool.Shared.Return(tempFromPool); + if (valueCopyFromPool != null) + ArrayPool.Shared.Return(valueCopyFromPool); + } - int size = PowBound(power, value.Length); - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, ref v); + private static void PowCore(Span value, int valueLength, Span temp, uint power, Span bits) + { + Debug.Assert(value.Length >= valueLength); + Debug.Assert(temp.Length == bits.Length); + Debug.Assert(value.Length == temp.Length); + + //save the result buffer to temp variable because bits buffer will change during buffer switch later + Span result = bits; + + bits[0] = 1; + int bitsLength = 1; + + // The basic pow algorithm using square-and-multiply. + while (power != 0) + { + if ((power & 1) == 1) + bitsLength = MultiplySelf(ref bits, bitsLength, value.Slice(0, valueLength), ref temp); + if (power != 1) + valueLength = SquareSelf(ref value, valueLength, ref temp); + power = power >> 1; + } + + bits.CopyTo(result); } - private static uint[] PowCore(uint power, ref BitsBuffer value) + private static int MultiplySelf(ref Span left, int leftLength, ReadOnlySpan right, ref Span temp) { - // Executes the basic pow algorithm. + Debug.Assert(leftLength <= left.Length); - int size = value.GetSize(); + int resultLength = leftLength + right.Length; - BitsBuffer temp = new BitsBuffer(size, 0); - BitsBuffer result = new BitsBuffer(size, 1); + if (leftLength >= right.Length) + { + Multiply(left.Slice(0, leftLength), right, temp.Slice(0, resultLength)); + } + else + { + Multiply(right, left.Slice(0, leftLength), temp.Slice(0, resultLength)); + } - PowCore(power, ref value, ref result, ref temp); + left.Clear(); + //switch buffers + Span t = left; + left = temp; + temp = t; + return ActualLength(left.Slice(0, resultLength)); + } - return result.GetBits(); + private static int SquareSelf(ref Span value, int valueLength, ref Span temp) + { + Debug.Assert(valueLength <= value.Length); + Debug.Assert(temp.Length >= valueLength + valueLength); + + int resultLength = valueLength + valueLength; + + Square(value.Slice(0, valueLength), temp.Slice(0, resultLength)); + + value.Clear(); + //switch buffers + Span t = value; + value = temp; + temp = t; + return ActualLength(value.Slice(0, resultLength)); } - private static int PowBound(uint power, int valueLength) + public static int PowBound(uint power, int valueLength) { // The basic pow algorithm, but instead of squaring // and multiplying we just sum up the lengths. @@ -73,21 +132,6 @@ private static int PowBound(uint power, int valueLength) return resultLength; } - private static void PowCore(uint power, ref BitsBuffer value, - ref BitsBuffer result, ref BitsBuffer temp) - { - // The basic pow algorithm using square-and-multiply. - - while (power != 0) - { - if ((power & 1) == 1) - result.MultiplySelf(ref value, ref temp); - if (power != 1) - value.SquareSelf(ref temp); - power = power >> 1; - } - } - public static uint Pow(uint value, uint power, uint modulus) { // The 32-bit modulus pow method for a 32-bit integer From 875cd16bc3e692e24baa590a519f334a5a6d3975 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 12:54:53 +0300 Subject: [PATCH 43/88] Use span for trivial modulus pow case --- .../src/System/Numerics/BigIntegerCalculator.PowMod.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index 4a08b0812891d..d9c624d36e353 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -140,10 +140,8 @@ public static uint Pow(uint value, uint power, uint modulus) return PowCore(power, modulus, value, 1); } - public static uint Pow(uint[] value, uint power, uint modulus) + public static uint Pow(ReadOnlySpan value, uint power, uint modulus) { - Debug.Assert(value != null); - // The 32-bit modulus pow method for a big integer // raised by a 32-bit integer... From 19086a616bc146ab4b6f0773be64de2784f29a31 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 12:58:02 +0300 Subject: [PATCH 44/88] Use span for trivial cases of pow modulus --- .../System/Numerics/BigIntegerCalculator.PowMod.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index d9c624d36e353..ba5df7f2781d0 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -149,21 +149,16 @@ public static uint Pow(ReadOnlySpan value, uint power, uint modulus) return PowCore(power, modulus, v, 1); } - public static uint Pow(uint value, uint[] power, uint modulus) + public static uint Pow(uint value, ReadOnlySpan power, uint modulus) { - Debug.Assert(power != null); - // The 32-bit modulus pow method for a 32-bit integer // raised by a big integer... return PowCore(power, modulus, value, 1); } - public static uint Pow(uint[] value, uint[] power, uint modulus) + public static uint Pow(ReadOnlySpan value, ReadOnlySpan power, uint modulus) { - Debug.Assert(value != null); - Debug.Assert(power != null); - // The 32-bit modulus pow method for a big integer // raised by a big integer... @@ -171,7 +166,7 @@ public static uint Pow(uint[] value, uint[] power, uint modulus) return PowCore(power, modulus, v, 1); } - private static uint PowCore(uint[] power, uint modulus, + private static uint PowCore(ReadOnlySpan power, uint modulus, ulong value, ulong result) { // The 32-bit modulus pow algorithm for all but From 64597dcefa688f4e03d58a907314db3dba57c8d2 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 13:07:38 +0300 Subject: [PATCH 45/88] Fixed buffer cleanup --- .../src/System/Numerics/BigIntegerCalculator.PowMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index ba5df7f2781d0..6883e3e66426f 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -35,8 +35,8 @@ stackalloc uint[bits.Length] Span valueCopy = bits.Length <= AllocationThreshold ? stackalloc uint[bits.Length] : (valueCopyFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); - valueCopy.Clear(); value.CopyTo(valueCopy); + valueCopy.Slice(value.Length).Clear(); PowCore(valueCopy, value.Length, temp, power, bits); From f86c6161f6a11eebebf7f4dc3e36bc0b4a407223 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 19:43:47 +0300 Subject: [PATCH 46/88] Reduced memory allocation of ModPow operations --- .../src/System/Numerics/BigInteger.cs | 37 ++- .../Numerics/BigIntegerCalculator.DivRem.cs | 19 +- .../Numerics/BigIntegerCalculator.GcdInv.cs | 1 - .../Numerics/BigIntegerCalculator.PowMod.cs | 218 ++++++++++-------- 4 files changed, 151 insertions(+), 124 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 2310aade6bdb9..e9d0ae85b4674 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -950,6 +950,8 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege bool trivialExponent = exponent._bits == null; bool trivialModulus = modulus._bits == null; + BigInteger result; + if (trivialModulus) { uint bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) : @@ -957,17 +959,40 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege trivialExponent ? BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) : BigIntegerCalculator.Pow(value._bits!, exponent._bits!, NumericsHelpers.Abs(modulus._sign)); - return value._sign < 0 && !exponent.IsEven ? -1 * bits : bits; + result = value._sign < 0 && !exponent.IsEven ? -1 * bits : bits; } else { - uint[] bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), modulus._bits!) : - trivialValue ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits!, modulus._bits!) : - trivialExponent ? BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent._sign), modulus._bits!) : - BigIntegerCalculator.Pow(value._bits!, exponent._bits!, modulus._bits!); + int size = (modulus._bits?.Length ?? 1) << 1; + uint[]? bitsFromPool = null; + Span bits = size <= StackAllocThreshold ? + stackalloc uint[size] + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + bits.Clear(); + if (trivialValue && trivialExponent) + { + BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), modulus._bits!, bits); + } + else if (trivialValue) + { + BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits!, modulus._bits!, bits); + } + else if (trivialExponent) + { + BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent._sign), modulus._bits!, bits); + } + else + { + BigIntegerCalculator.Pow(value._bits!, exponent._bits!, modulus._bits!, bits); + } + + result = new BigInteger(bits, value._sign < 0 && !exponent.IsEven); - return new BigInteger(bits, value._sign < 0 && !exponent.IsEven); + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); } + + return result; } public static BigInteger Pow(BigInteger value, int exponent) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 25c5203e3d73b..5975cc920b6c5 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -98,29 +98,12 @@ stackalloc uint[left.Length] ArrayPool.Shared.Return(leftCopyFromPool); } - public static uint[] Remainder(ReadOnlySpan left, ReadOnlySpan right) - { - Debug.Assert(left.Length >= 1); - Debug.Assert(right.Length >= 1); - Debug.Assert(left.Length >= right.Length); - - // Same as above, but only returning the remainder. - - // NOTE: left will get overwritten, we need a local copy - - uint[] localLeft = left.ToArray(); - - Divide(localLeft, right, default); - - return localLeft; - } - public static void Remainder(ReadOnlySpan left, ReadOnlySpan right, Span remainder) { Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); - Debug.Assert(remainder.Length == left.Length); + Debug.Assert(remainder.Length >= left.Length); // Same as above, but only returning the remainder. diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index dd162fa942c73..9c995d7087409 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -198,7 +198,6 @@ private static void Gcd(Span left, Span right) private static int Reduce(Span bits, ReadOnlySpan modulus) { // Executes a modulo operation using the divide operation. - // Thus, no need of any switching here, happens in-line. if (bits.Length >= modulus.Length) { diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index 6883e3e66426f..bcfbf10aec7e8 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -24,7 +24,6 @@ public static void Pow(ReadOnlySpan value, uint power, Span bits) { Debug.Assert(bits.Length == PowBound(power, value.Length)); - // The basic pow method for a big integer. uint[]? tempFromPool = null; Span temp = bits.Length <= AllocationThreshold ? stackalloc uint[bits.Length] @@ -38,7 +37,7 @@ stackalloc uint[bits.Length] value.CopyTo(valueCopy); valueCopy.Slice(value.Length).Clear(); - PowCore(valueCopy, value.Length, temp, power, bits); + PowCore(valueCopy, value.Length, temp, power, bits).CopyTo(bits); if (tempFromPool != null) ArrayPool.Shared.Return(tempFromPool); @@ -46,29 +45,26 @@ stackalloc uint[bits.Length] ArrayPool.Shared.Return(valueCopyFromPool); } - private static void PowCore(Span value, int valueLength, Span temp, uint power, Span bits) + private static Span PowCore(Span value, int valueLength, Span temp, uint power, Span result) { Debug.Assert(value.Length >= valueLength); - Debug.Assert(temp.Length == bits.Length); + Debug.Assert(temp.Length == result.Length); Debug.Assert(value.Length == temp.Length); - //save the result buffer to temp variable because bits buffer will change during buffer switch later - Span result = bits; - - bits[0] = 1; + result[0] = 1; int bitsLength = 1; // The basic pow algorithm using square-and-multiply. while (power != 0) { if ((power & 1) == 1) - bitsLength = MultiplySelf(ref bits, bitsLength, value.Slice(0, valueLength), ref temp); + bitsLength = MultiplySelf(ref result, bitsLength, value.Slice(0, valueLength), ref temp); if (power != 1) valueLength = SquareSelf(ref value, valueLength, ref temp); power = power >> 1; } - bits.CopyTo(result); + return result; } private static int MultiplySelf(ref Span left, int leftLength, ReadOnlySpan right, ref Span temp) @@ -205,84 +201,110 @@ private static uint PowCore(uint power, uint modulus, return (uint)(result % modulus); } - public static uint[] Pow(uint value, uint power, uint[] modulus) + public static void Pow(uint value, uint power, ReadOnlySpan modulus, Span bits) { - Debug.Assert(modulus != null); - - // The big modulus pow method for a 32-bit integer - // raised by a 32-bit integer... - - int size = modulus.Length + modulus.Length; - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, modulus, ref v); + Pow(value != 0U ? MemoryMarshal.CreateReadOnlySpan(ref value, 1) : default, power, modulus, bits); } - public static uint[] Pow(uint[] value, uint power, uint[] modulus) + public static void Pow(ReadOnlySpan value, uint power, ReadOnlySpan modulus, Span bits) { - Debug.Assert(value != null); - Debug.Assert(modulus != null); + Debug.Assert(!modulus.IsEmpty); + Debug.Assert(bits.Length == modulus.Length + modulus.Length); // The big modulus pow method for a big integer // raised by a 32-bit integer... + uint[]? valueCopyFromPool = null; + int size = Math.Max(value.Length, bits.Length); + Span valueCopy = size <= AllocationThreshold ? + stackalloc uint[size] + : (valueCopyFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + valueCopy.Clear(); + if (value.Length > modulus.Length) - value = Remainder(value, modulus); + { + Remainder(value, modulus, valueCopy); + } + else + { + value.CopyTo(valueCopy); + } - int size = modulus.Length + modulus.Length; - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, modulus, ref v); - } + uint[]? tempFromPool = null; + Span temp = bits.Length <= AllocationThreshold ? + stackalloc uint[bits.Length] + : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); + temp.Clear(); - public static uint[] Pow(uint value, uint[] power, uint[] modulus) - { - Debug.Assert(power != null); - Debug.Assert(modulus != null); + PowCore(valueCopy, ActualLength(valueCopy), power, modulus, temp, bits); - // The big modulus pow method for a 32-bit integer - // raised by a big integer... + if (valueCopyFromPool != null) + ArrayPool.Shared.Return(valueCopyFromPool); + if (tempFromPool != null) + ArrayPool.Shared.Return(tempFromPool); + } - int size = modulus.Length + modulus.Length; - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, modulus, ref v); + public static void Pow(uint value, ReadOnlySpan power, ReadOnlySpan modulus, Span bits) + { + Pow(value != 0U ? MemoryMarshal.CreateReadOnlySpan(ref value, 1) : default, power, modulus, bits); } - public static uint[] Pow(uint[] value, uint[] power, uint[] modulus) + public static void Pow(ReadOnlySpan value, ReadOnlySpan power, ReadOnlySpan modulus, Span bits) { - Debug.Assert(value != null); - Debug.Assert(power != null); - Debug.Assert(modulus != null); + Debug.Assert(!modulus.IsEmpty); + Debug.Assert(bits.Length == modulus.Length + modulus.Length); // The big modulus pow method for a big integer // raised by a big integer... + int size = Math.Max(value.Length, bits.Length); + uint[]? valueCopyFromPool = null; + Span valueCopy = size <= AllocationThreshold ? + stackalloc uint[size] + : (valueCopyFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + valueCopy.Clear(); + if (value.Length > modulus.Length) - value = Remainder(value, modulus); + { + Remainder(value, modulus, valueCopy); + } + else + { + value.CopyTo(valueCopy); + } + + uint[]? tempFromPool = null; + Span temp = bits.Length <= AllocationThreshold ? + stackalloc uint[bits.Length] + : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); + temp.Clear(); + + PowCore(power, modulus, valueCopy, ActualLength(valueCopy), temp, bits); - int size = modulus.Length + modulus.Length; - BitsBuffer v = new BitsBuffer(size, value); - return PowCore(power, modulus, ref v); + if (valueCopyFromPool != null) + ArrayPool.Shared.Return(valueCopyFromPool); + if (tempFromPool != null) + ArrayPool.Shared.Return(tempFromPool); } // Mutable for unit testing... private static int ReducerThreshold = 32; - private static uint[] PowCore(uint[] power, uint[] modulus, - ref BitsBuffer value) + private static void PowCore(ReadOnlySpan power, ReadOnlySpan modulus, + Span value, int valueLength, + Span temp, Span bits) { // Executes the big pow algorithm. - int size = value.GetSize(); - - BitsBuffer temp = new BitsBuffer(size, 0); - BitsBuffer result = new BitsBuffer(size, 1); + bits[0] = 1; if (modulus.Length < ReducerThreshold) { - PowCore(power, modulus, ref value, ref result, ref temp); + PowCore(power, modulus, value, valueLength, bits, 1, temp).CopyTo(bits); } else { - size = modulus.Length * 2 + 1; + int size = modulus.Length * 2 + 1; uint[]? rFromPool = null; Span r = size <= AllocationThreshold ? stackalloc uint[size] @@ -314,7 +336,7 @@ stackalloc uint[size] if (rFromPool != null) ArrayPool.Shared.Return(rFromPool); - PowCore(power, reducer, ref value, ref result, ref temp); + PowCore(power, reducer, value, valueLength, bits, 1, temp).CopyTo(bits); if (muFromPool != null) ArrayPool.Shared.Return(muFromPool); @@ -323,27 +345,21 @@ stackalloc uint[size] if (q2FromPool != null) ArrayPool.Shared.Return(q2FromPool); } - - return result.GetBits(); } - private static uint[] PowCore(uint power, uint[] modulus, - ref BitsBuffer value) + private static void PowCore(Span value, int valueLength, uint power, ReadOnlySpan modulus, + Span temp, Span bits) { // Executes the big pow algorithm. - - int size = value.GetSize(); - - BitsBuffer temp = new BitsBuffer(size, 0); - BitsBuffer result = new BitsBuffer(size, 1); + bits[0] = 1; if (modulus.Length < ReducerThreshold) { - PowCore(power, modulus, ref value, ref result, ref temp); + PowCore(power, modulus, value, valueLength, bits, 1, temp).CopyTo(bits); } else { - size = modulus.Length * 2 + 1; + int size = modulus.Length * 2 + 1; uint[]? rFromPool = null; Span r = size <= AllocationThreshold ? stackalloc uint[size] @@ -375,7 +391,7 @@ stackalloc uint[size] if (rFromPool != null) ArrayPool.Shared.Return(rFromPool); - PowCore(power, reducer, ref value, ref result, ref temp); + PowCore(power, reducer, value, valueLength, bits, 1, temp).CopyTo(bits); if (muFromPool != null) ArrayPool.Shared.Return(muFromPool); @@ -384,13 +400,12 @@ stackalloc uint[size] if (q2FromPool != null) ArrayPool.Shared.Return(q2FromPool); } - - return result.GetBits(); } - private static void PowCore(uint[] power, uint[] modulus, - ref BitsBuffer value, ref BitsBuffer result, - ref BitsBuffer temp) + private static Span PowCore(ReadOnlySpan power, ReadOnlySpan modulus, + Span value, int valueLength, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for all but // the last power limb using square-and-multiply. @@ -405,22 +420,22 @@ private static void PowCore(uint[] power, uint[] modulus, { if ((p & 1) == 1) { - result.MultiplySelf(ref value, ref temp); - result.Reduce(modulus); + resultLength = MultiplySelf(ref result, resultLength, value.Slice(0, valueLength), ref temp); + resultLength = Reduce(result.Slice(0, resultLength), modulus); } - value.SquareSelf(ref temp); - value.Reduce(modulus); + valueLength = SquareSelf(ref value, valueLength, ref temp); + valueLength = Reduce(value.Slice(0, valueLength), modulus); p = p >> 1; } } - PowCore(power[power.Length - 1], modulus, ref value, ref result, - ref temp); + return PowCore(power[power.Length - 1], modulus, value, valueLength, result, resultLength, temp); } - private static void PowCore(uint power, uint[] modulus, - ref BitsBuffer value, ref BitsBuffer result, - ref BitsBuffer temp) + private static Span PowCore(uint power, ReadOnlySpan modulus, + Span value, int valueLength, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for the last or // the only power limb using square-and-multiply. @@ -432,21 +447,24 @@ private static void PowCore(uint power, uint[] modulus, { if ((power & 1) == 1) { - result.MultiplySelf(ref value, ref temp); - result.Reduce(modulus); + resultLength = MultiplySelf(ref result, resultLength, value.Slice(0, valueLength), ref temp); + resultLength = Reduce(result.Slice(0, resultLength), modulus); } if (power != 1) { - value.SquareSelf(ref temp); - value.Reduce(modulus); + valueLength = SquareSelf(ref value, valueLength, ref temp); + valueLength = Reduce(value.Slice(0, valueLength), modulus); } power = power >> 1; } + + return result; } - private static void PowCore(uint[] power, in FastReducer reducer, - ref BitsBuffer value, ref BitsBuffer result, - ref BitsBuffer temp) + private static Span PowCore(ReadOnlySpan power, in FastReducer reducer, + Span value, int valueLength, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for all but // the last power limb using square-and-multiply. @@ -461,22 +479,22 @@ private static void PowCore(uint[] power, in FastReducer reducer, { if ((p & 1) == 1) { - result.MultiplySelf(ref value, ref temp); - result.Reduce(reducer); + resultLength = MultiplySelf(ref result, resultLength, value.Slice(0, valueLength), ref temp); + resultLength = reducer.Reduce(result.Slice(0, resultLength)); } - value.SquareSelf(ref temp); - value.Reduce(reducer); + valueLength = SquareSelf(ref value, valueLength, ref temp); + valueLength = reducer.Reduce(value.Slice(0, valueLength)); p = p >> 1; } } - PowCore(power[power.Length - 1], reducer, ref value, ref result, - ref temp); + return PowCore(power[power.Length - 1], reducer, value, valueLength, result, resultLength, temp); } - private static void PowCore(uint power, in FastReducer reducer, - ref BitsBuffer value, ref BitsBuffer result, - ref BitsBuffer temp) + private static Span PowCore(uint power, in FastReducer reducer, + Span value, int valueLength, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for the last or // the only power limb using square-and-multiply. @@ -488,16 +506,18 @@ private static void PowCore(uint power, in FastReducer reducer, { if ((power & 1) == 1) { - result.MultiplySelf(ref value, ref temp); - result.Reduce(reducer); + resultLength = MultiplySelf(ref result, resultLength, value.Slice(0, valueLength), ref temp); + resultLength = reducer.Reduce(result.Slice(0, resultLength)); } if (power != 1) { - value.SquareSelf(ref temp); - value.Reduce(reducer); + valueLength = SquareSelf(ref value, valueLength, ref temp); + valueLength = reducer.Reduce(value.Slice(0, valueLength)); } power = power >> 1; } + + return result; } } } From 7509c5925c1ef46907266ea43b6bff184a410c4c Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 19:45:45 +0300 Subject: [PATCH 47/88] BitsBuffer replaced with span --- .../src/System.Runtime.Numerics.csproj | 1 - .../BigIntegerCalculator.BitsBuffer.cs | 153 ------------------ 2 files changed, 154 deletions(-) delete mode 100644 src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs diff --git a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj index fd40c4a5fd0ea..cf77c8b23f28d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj +++ b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj @@ -8,7 +8,6 @@ - diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs deleted file mode 100644 index bc9eb3d7bec93..0000000000000 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; - -namespace System.Numerics -{ - // ATTENTION: always pass BitsBuffer by reference, - // it's a structure for performance reasons. Furthermore - // it's a mutable one, so use it only with care! - - internal static partial class BigIntegerCalculator - { - // To spare memory allocations a buffer helps reusing memory! - // We just create the target array twice and switch between every - // operation. In order to not compute unnecessarily with all those - // leading zeros we take care of the current actual length. - - internal struct BitsBuffer - { - private uint[] _bits; - private int _length; - - public BitsBuffer(int size, uint value) - { - Debug.Assert(size >= 1); - - _bits = new uint[size]; - _length = value != 0 ? 1 : 0; - - _bits[0] = value; - } - - public BitsBuffer(int size, uint[] value) - { - Debug.Assert(value != null); - Debug.Assert(size >= ActualLength(value)); - - _bits = new uint[size]; - _length = ActualLength(value); - - Array.Copy(value, _bits, _length); - } - - public void MultiplySelf(ref BitsBuffer value, ref BitsBuffer temp) - { - Debug.Assert(temp._length == 0); - Debug.Assert(_length + value._length <= temp._bits.Length); - - // Executes a multiplication for this and value, writes the - // result to temp. Switches this and temp arrays afterwards. - - if (_length < value._length) - { - Multiply(new ReadOnlySpan(value._bits, 0, value._length), - new ReadOnlySpan(_bits, 0, _length), - new Span(temp._bits, 0, _length + value._length)); - } - else - { - Multiply(new ReadOnlySpan(_bits, 0, _length), - new ReadOnlySpan(value._bits, 0, value._length), - new Span(temp._bits, 0, _length + value._length)); - } - - Apply(ref temp, _length + value._length); - } - - public void SquareSelf(ref BitsBuffer temp) - { - Debug.Assert(temp._length == 0); - Debug.Assert(_length + _length <= temp._bits.Length); - - // Executes a square for this, writes the result to temp. - // Switches this and temp arrays afterwards. - - Square(new ReadOnlySpan(_bits, 0, _length), - new Span(temp._bits, 0, _length + _length)); - - Apply(ref temp, _length + _length); - } - - public void Reduce(in FastReducer reducer) - { - // Executes a modulo operation using an optimized reducer. - // Thus, no need of any switching here, happens in-line. - - _length = reducer.Reduce(_bits.AsSpan(0, _length)); - } - - public void Reduce(uint[] modulus) - { - Debug.Assert(modulus != null); - - // Executes a modulo operation using the divide operation. - // Thus, no need of any switching here, happens in-line. - - if (_length >= modulus.Length) - { - Divide(new Span(_bits, 0, _length), modulus, default); - - _length = ActualLength(new ReadOnlySpan(_bits, 0, modulus.Length)); - } - } - - public uint[] GetBits() - { - return _bits; - } - - public int GetSize() - { - return _bits.Length; - } - - public int GetLength() - { - return _length; - } - - public void Refresh(int maxLength) - { - Debug.Assert(_bits.Length >= maxLength); - - if (_length > maxLength) - { - // Ensure leading zeros - Array.Clear(_bits, maxLength, _length - maxLength); - } - - _length = ActualLength(new ReadOnlySpan(_bits, 0, maxLength)); - } - - private void Apply(ref BitsBuffer temp, int maxLength) - { - Debug.Assert(temp._length == 0); - Debug.Assert(maxLength <= temp._bits.Length); - - // Resets this and switches this and temp afterwards. - // The caller assumed an empty temp, the next will too. - - Array.Clear(_bits, 0, _length); - - uint[] t = temp._bits; - temp._bits = _bits; - _bits = t; - - _length = ActualLength(new ReadOnlySpan(_bits, 0, maxLength)); - } - } - } -} From 61e028b5faa6c3fcbd7ec13aa3db9b8617d6ebb2 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 19:47:14 +0300 Subject: [PATCH 48/88] Reorganize utility methods --- .../System/Numerics/BigIntegerCalculator.GcdInv.cs | 13 ------------- .../System/Numerics/BigIntegerCalculator.Utils.cs | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 9c995d7087409..2950b3f5d9596 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -195,19 +195,6 @@ private static void Gcd(Span left, Span right) left.CopyTo(result); } - private static int Reduce(Span bits, ReadOnlySpan modulus) - { - // Executes a modulo operation using the divide operation. - - if (bits.Length >= modulus.Length) - { - Divide(bits, modulus, default); - - return ActualLength(bits.Slice(0, modulus.Length)); - } - return bits.Length; - } - private static int Overwrite(Span buffer, ulong value) { Debug.Assert(buffer.Length >= 2); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs index 3b62505ad3613..c28d8534203ac 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -40,5 +40,18 @@ private static int ActualLength(ReadOnlySpan value) --length; return length; } + + private static int Reduce(Span bits, ReadOnlySpan modulus) + { + // Executes a modulo operation using the divide operation. + + if (bits.Length >= modulus.Length) + { + Divide(bits, modulus, default); + + return ActualLength(bits.Slice(0, modulus.Length)); + } + return bits.Length; + } } } \ No newline at end of file From 4ff0bd55fc2bd1996a8beacb7e49ca1f634ddce0 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 19:49:40 +0300 Subject: [PATCH 49/88] Reduced visibility scope --- .../src/System/Numerics/BigIntegerCalculator.FastReducer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 4b230db2ad6ac..92419a6cd0414 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -14,7 +14,7 @@ internal static partial class BigIntegerCalculator // see https://en.wikipedia.org/wiki/Barrett_reduction - internal readonly ref struct FastReducer + private readonly ref struct FastReducer { private readonly ReadOnlySpan _modulus; private readonly ReadOnlySpan _mu; From 9e2242e50c7bae129d0bc87a0a2753fd883464b1 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 19:54:52 +0300 Subject: [PATCH 50/88] Removed temporary code for bits cleanup --- .../System/Numerics/BigIntegerCalculator.FastReducer.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 92419a6cd0414..8be29fc064cc6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -59,7 +59,10 @@ public int Reduce(Span value) int l2 = DivMul(_q1.Slice(0, l1), _modulus, _q2, _modulus.Length + 1); // Let v = (v - q2) % 2^(k+1) - i*m - return SubMod(value, _q2.Slice(0, l2), _modulus, _modulus.Length + 1); + var length = SubMod(value, _q2.Slice(0, l2), _modulus, _modulus.Length + 1); + value.Slice(length).Clear(); + + return length; } private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Span bits, int k) @@ -104,8 +107,6 @@ private static int SubMod(Span left, ReadOnlySpan right, ReadOnlySpa Debug.Assert(!left.IsEmpty); Debug.Assert(!right.IsEmpty); - Span originalLeft = left; //TODO: Should be removed later - // Executes the subtraction algorithm for left and right, // but considers only the first k limbs, which is equivalent to // preceding reduction by 2^(32*k). Furthermore, if left is @@ -125,8 +126,6 @@ private static int SubMod(Span left, ReadOnlySpan right, ReadOnlySpa left = left.Slice(0, ActualLength(left)); } - originalLeft.Slice(left.Length, originalLeft.Length - left.Length).Clear(); //TODO: Should be removed later - return left.Length; } } From 98b7fa858399fc9d044c476940a81c84aa475cfe Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 19:56:33 +0300 Subject: [PATCH 51/88] Fill leading bits with zeroes --- .../src/System/Numerics/BigIntegerCalculator.FastReducer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 8be29fc064cc6..13f8011cb6da2 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -60,7 +60,8 @@ public int Reduce(Span value) // Let v = (v - q2) % 2^(k+1) - i*m var length = SubMod(value, _q2.Slice(0, l2), _modulus, _modulus.Length + 1); - value.Slice(length).Clear(); + value = value.Slice(length); + value.Clear(); return length; } From f5a5b9efafdcb8b28c300282e31eda7f515cc830 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 20:08:11 +0300 Subject: [PATCH 52/88] Unify order of parameters --- .../src/System/Numerics/BigIntegerCalculator.PowMod.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index bcfbf10aec7e8..579e1a810e471 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -150,7 +150,7 @@ public static uint Pow(uint value, ReadOnlySpan power, uint modulus) // The 32-bit modulus pow method for a 32-bit integer // raised by a big integer... - return PowCore(power, modulus, value, 1); + return PowCore(value, power, modulus, 1); } public static uint Pow(ReadOnlySpan value, ReadOnlySpan power, uint modulus) @@ -159,11 +159,10 @@ public static uint Pow(ReadOnlySpan value, ReadOnlySpan power, uint // raised by a big integer... uint v = Remainder(value, modulus); - return PowCore(power, modulus, v, 1); + return PowCore(v, power, modulus, 1); } - private static uint PowCore(ReadOnlySpan power, uint modulus, - ulong value, ulong result) + private static uint PowCore(ulong value, ReadOnlySpan power, uint modulus, ulong result) { // The 32-bit modulus pow algorithm for all but // the last power limb using square-and-multiply. From f71370361459f283038499e960608a7d59e6073a Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 20:25:43 +0300 Subject: [PATCH 53/88] Unify order of parameters --- .../src/System/Numerics/BigIntegerCalculator.PowMod.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index 579e1a810e471..f83c0bfd26dd6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -133,7 +133,7 @@ public static uint Pow(uint value, uint power, uint modulus) // The 32-bit modulus pow method for a 32-bit integer // raised by a 32-bit integer... - return PowCore(power, modulus, value, 1); + return PowCore(value, power, modulus, 1); } public static uint Pow(ReadOnlySpan value, uint power, uint modulus) @@ -142,7 +142,7 @@ public static uint Pow(ReadOnlySpan value, uint power, uint modulus) // raised by a 32-bit integer... uint v = Remainder(value, modulus); - return PowCore(power, modulus, v, 1); + return PowCore(v, power, modulus, 1); } public static uint Pow(uint value, ReadOnlySpan power, uint modulus) @@ -179,11 +179,10 @@ private static uint PowCore(ulong value, ReadOnlySpan power, uint modulus, } } - return PowCore(power[power.Length - 1], modulus, value, result); + return PowCore(value, power[power.Length - 1], modulus, result); } - private static uint PowCore(uint power, uint modulus, - ulong value, ulong result) + private static uint PowCore(ulong value, uint power, uint modulus, ulong result) { // The 32-bit modulus pow algorithm for the last or // the only power limb using square-and-multiply. From 0f1f935cee59b3e85646a9d0c89e649561f22a27 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 20:32:38 +0300 Subject: [PATCH 54/88] Unify order of parameters --- .../Numerics/BigIntegerCalculator.PowMod.cs | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index f83c0bfd26dd6..49472c025e094 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -199,12 +199,14 @@ private static uint PowCore(ulong value, uint power, uint modulus, ulong result) return (uint)(result % modulus); } - public static void Pow(uint value, uint power, ReadOnlySpan modulus, Span bits) + public static void Pow(uint value, uint power, + ReadOnlySpan modulus, Span bits) { Pow(value != 0U ? MemoryMarshal.CreateReadOnlySpan(ref value, 1) : default, power, modulus, bits); } - public static void Pow(ReadOnlySpan value, uint power, ReadOnlySpan modulus, Span bits) + public static void Pow(ReadOnlySpan value, uint power, + ReadOnlySpan modulus, Span bits) { Debug.Assert(!modulus.IsEmpty); Debug.Assert(bits.Length == modulus.Length + modulus.Length); @@ -242,12 +244,14 @@ stackalloc uint[bits.Length] ArrayPool.Shared.Return(tempFromPool); } - public static void Pow(uint value, ReadOnlySpan power, ReadOnlySpan modulus, Span bits) + public static void Pow(uint value, ReadOnlySpan power, + ReadOnlySpan modulus, Span bits) { Pow(value != 0U ? MemoryMarshal.CreateReadOnlySpan(ref value, 1) : default, power, modulus, bits); } - public static void Pow(ReadOnlySpan value, ReadOnlySpan power, ReadOnlySpan modulus, Span bits) + public static void Pow(ReadOnlySpan value, ReadOnlySpan power, + ReadOnlySpan modulus, Span bits) { Debug.Assert(!modulus.IsEmpty); Debug.Assert(bits.Length == modulus.Length + modulus.Length); @@ -277,7 +281,7 @@ stackalloc uint[bits.Length] : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); temp.Clear(); - PowCore(power, modulus, valueCopy, ActualLength(valueCopy), temp, bits); + PowCore(valueCopy, ActualLength(valueCopy), power, modulus, temp, bits); if (valueCopyFromPool != null) ArrayPool.Shared.Return(valueCopyFromPool); @@ -288,8 +292,8 @@ stackalloc uint[bits.Length] // Mutable for unit testing... private static int ReducerThreshold = 32; - private static void PowCore(ReadOnlySpan power, ReadOnlySpan modulus, - Span value, int valueLength, + private static void PowCore(Span value, int valueLength, + ReadOnlySpan power, ReadOnlySpan modulus, Span temp, Span bits) { // Executes the big pow algorithm. @@ -298,7 +302,7 @@ private static void PowCore(ReadOnlySpan power, ReadOnlySpan modulus if (modulus.Length < ReducerThreshold) { - PowCore(power, modulus, value, valueLength, bits, 1, temp).CopyTo(bits); + PowCore(value, valueLength, power, modulus, bits, 1, temp).CopyTo(bits); } else { @@ -334,7 +338,7 @@ stackalloc uint[size] if (rFromPool != null) ArrayPool.Shared.Return(rFromPool); - PowCore(power, reducer, value, valueLength, bits, 1, temp).CopyTo(bits); + PowCore(value, valueLength, power, reducer, bits, 1, temp).CopyTo(bits); if (muFromPool != null) ArrayPool.Shared.Return(muFromPool); @@ -345,15 +349,16 @@ stackalloc uint[size] } } - private static void PowCore(Span value, int valueLength, uint power, ReadOnlySpan modulus, - Span temp, Span bits) + private static void PowCore(Span value, int valueLength, + uint power, ReadOnlySpan modulus, + Span temp, Span bits) { // Executes the big pow algorithm. bits[0] = 1; if (modulus.Length < ReducerThreshold) { - PowCore(power, modulus, value, valueLength, bits, 1, temp).CopyTo(bits); + PowCore(value, valueLength, power, modulus, bits, 1, temp).CopyTo(bits); } else { @@ -389,7 +394,7 @@ stackalloc uint[size] if (rFromPool != null) ArrayPool.Shared.Return(rFromPool); - PowCore(power, reducer, value, valueLength, bits, 1, temp).CopyTo(bits); + PowCore(value, valueLength, power, reducer, bits, 1, temp).CopyTo(bits); if (muFromPool != null) ArrayPool.Shared.Return(muFromPool); @@ -400,10 +405,10 @@ stackalloc uint[size] } } - private static Span PowCore(ReadOnlySpan power, ReadOnlySpan modulus, - Span value, int valueLength, - Span result, int resultLength, - Span temp) + private static Span PowCore(Span value, int valueLength, + ReadOnlySpan power, ReadOnlySpan modulus, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for all but // the last power limb using square-and-multiply. @@ -427,13 +432,13 @@ private static Span PowCore(ReadOnlySpan power, ReadOnlySpan m } } - return PowCore(power[power.Length - 1], modulus, value, valueLength, result, resultLength, temp); + return PowCore(value, valueLength, power[power.Length - 1], modulus, result, resultLength, temp); } - private static Span PowCore(uint power, ReadOnlySpan modulus, - Span value, int valueLength, - Span result, int resultLength, - Span temp) + private static Span PowCore(Span value, int valueLength, + uint power, ReadOnlySpan modulus, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for the last or // the only power limb using square-and-multiply. @@ -459,10 +464,10 @@ private static Span PowCore(uint power, ReadOnlySpan modulus, return result; } - private static Span PowCore(ReadOnlySpan power, in FastReducer reducer, - Span value, int valueLength, - Span result, int resultLength, - Span temp) + private static Span PowCore(Span value, int valueLength, + ReadOnlySpan power, in FastReducer reducer, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for all but // the last power limb using square-and-multiply. @@ -486,13 +491,13 @@ private static Span PowCore(ReadOnlySpan power, in FastReducer reduc } } - return PowCore(power[power.Length - 1], reducer, value, valueLength, result, resultLength, temp); + return PowCore(value, valueLength, power[power.Length - 1], reducer, result, resultLength, temp); } - private static Span PowCore(uint power, in FastReducer reducer, - Span value, int valueLength, - Span result, int resultLength, - Span temp) + private static Span PowCore(Span value, int valueLength, + uint power, in FastReducer reducer, + Span result, int resultLength, + Span temp) { // The big modulus pow algorithm for the last or // the only power limb using square-and-multiply. From c9ba177ca3abea5d44c54755582acd7832ace05b Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 20:42:50 +0300 Subject: [PATCH 55/88] Reuse the same local var --- .../src/System/Numerics/BigInteger.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index e9d0ae85b4674..a40d30368257b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1010,7 +1010,7 @@ public static BigInteger Pow(BigInteger value, int exponent) bool trivialValue = value._bits == null; uint power = NumericsHelpers.Abs(exponent); - uint[]? valuesFromPool = null; + uint[]? bitsFromPool = null; BigInteger result; if (trivialValue) @@ -1025,7 +1025,7 @@ public static BigInteger Pow(BigInteger value, int exponent) int size = BigIntegerCalculator.PowBound(power, 1); Span bits = size <= StackAllocThreshold ? stackalloc uint[size] - : (valuesFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); bits.Clear(); BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), power, bits); @@ -1036,15 +1036,15 @@ stackalloc uint[size] int size = BigIntegerCalculator.PowBound(power, value._bits!.Length); Span bits = size <= StackAllocThreshold ? stackalloc uint[size] - : (valuesFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); bits.Clear(); BigIntegerCalculator.Pow(value._bits, power, bits); result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0); } - if (valuesFromPool != null) - ArrayPool.Shared.Return(valuesFromPool); + if (bitsFromPool != null) + ArrayPool.Shared.Return(bitsFromPool); return result; } @@ -2388,12 +2388,13 @@ stackalloc uint[size] // and therefore the bigger one return s_bnZeroInt; } + + uint[]? quotientFromPool = null; if (trivialDivisor) { Debug.Assert(dividend._bits != null); - uint[]? quotientFromPool = null; int size = dividend._bits.Length; Span quotient = size <= StackAllocThreshold ? stackalloc uint[size] @@ -2420,7 +2421,6 @@ stackalloc uint[size] } else { - uint[]? quotientFromPool = null; int size = dividend._bits.Length - divisor._bits.Length + 1; Span quotient = size < StackAllocThreshold ? stackalloc uint[size] From e1afe77063c9d95a3622d3aba1b88123858a7153 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 2 May 2020 20:51:36 +0300 Subject: [PATCH 56/88] Removed whtespace --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index a40d30368257b..81486da639922 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -2388,7 +2388,7 @@ stackalloc uint[size] // and therefore the bigger one return s_bnZeroInt; } - + uint[]? quotientFromPool = null; if (trivialDivisor) From f085cc4a0e2b4a6261480320c048a79ccf7eddd1 Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 3 May 2020 16:41:43 +0300 Subject: [PATCH 57/88] Code review feedback --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 1 - .../src/System/Numerics/BigIntegerCalculator.DivRem.cs | 2 +- .../src/System/Numerics/BigIntegerCalculator.GcdInv.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 81486da639922..6a88266e22ddc 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -554,7 +554,6 @@ private BigInteger(Span value) _sign = +1; value = value.Slice(0, dwordCount); _bits = value.ToArray(); - value.CopyTo(_bits); AssertValid(); return; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 5975cc920b6c5..87b56e78c96e0 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -87,7 +87,7 @@ public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Spa // NOTE: left will get overwritten, we need a local copy // However, mutated left is not used afterwards, so use array pooling or stack alloc - Span leftCopy = left.Length < AllocationThreshold ? + Span leftCopy = left.Length <= AllocationThreshold ? stackalloc uint[left.Length] : (leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).AsSpan(0, left.Length); left.CopyTo(leftCopy); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 2950b3f5d9596..8502622515f66 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -208,8 +208,8 @@ private static int Overwrite(Span buffer, ulong value) uint lo = unchecked((uint)value); uint hi = (uint)(value >> 32); - buffer[0] = lo; buffer[1] = hi; + buffer[0] = lo; return hi != 0 ? 2 : lo != 0 ? 1 : 0; } From 30132f0500bc018dd97bcd277b23333b4270cba6 Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 3 May 2020 16:57:49 +0300 Subject: [PATCH 58/88] Avoid misleading with Span.CopyTo --- .../src/System/Numerics/BigInteger.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 6a88266e22ddc..ebc3151146b13 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1494,7 +1494,7 @@ private enum GetBytesMode { AllocateArray, Count, Span } /// /// Pre-allocated buffer by the caller. /// The actual number of copied elements. - private int CopyTo(Span buffer) + private int WriteTo(Span buffer) { Debug.Assert(_bits is null || _sign == 0 ? buffer.Length == 2 : buffer.Length >= _bits.Length + 1); @@ -1973,14 +1973,14 @@ public static explicit operator decimal(BigInteger value) Span x = size <= StackAllocThreshold ? stackalloc uint[size] : leftBufferFromPool = ArrayPool.Shared.Rent(size); - x = x.Slice(0, left.CopyTo(x)); + x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; Span y = size <= StackAllocThreshold ? stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); - y = y.Slice(0, right.CopyTo(y)); + y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); @@ -2029,14 +2029,14 @@ stackalloc uint[size] Span x = size <= StackAllocThreshold ? stackalloc uint[size] : leftBufferFromPool = ArrayPool.Shared.Rent(size); - x = x.Slice(0, left.CopyTo(x)); + x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; Span y = size <= StackAllocThreshold ? stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); - y = y.Slice(0, right.CopyTo(y)); + y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); @@ -2080,14 +2080,14 @@ stackalloc uint[size] Span x = size <= StackAllocThreshold ? stackalloc uint[size] : leftBufferFromPool = ArrayPool.Shared.Rent(size); - x = x.Slice(0, left.CopyTo(x)); + x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; Span y = size <= StackAllocThreshold ? stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); - y = y.Slice(0, right.CopyTo(y)); + y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); From 0344cb245e15a488d33e88f61b8638c98733be9c Mon Sep 17 00:00:00 2001 From: SRV Date: Sun, 3 May 2020 17:25:30 +0300 Subject: [PATCH 59/88] Clarify purpose of slicing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- .../src/System/Numerics/BigIntegerCalculator.GcdInv.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 8502622515f66..894fd441a6e20 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -201,7 +201,7 @@ private static int Overwrite(Span buffer, ulong value) if (buffer.Length > 2) { - // Ensure leading zeros + // Ensure leading zeros in little-endian buffer.Slice(2).Clear(); } From 4c6dd0b572af43095a6851ffacc3f06ceaf4ebb3 Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 3 May 2020 17:26:20 +0300 Subject: [PATCH 60/88] Replace mentioning of uint[] in comments --- .../src/System/Numerics/BigInteger.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index ebc3151146b13..6f1f5503ec73b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -483,8 +483,8 @@ internal BigInteger(ReadOnlySpan value, bool negative) { int len; - // Try to conserve space as much as possible by checking for wasted leading uint[] entries - // sometimes the uint[] has leading zeros from bit manipulation operations & and ^ + // Try to conserve space as much as possible by checking for wasted leading span entries + // sometimes the span has leading zeros from bit manipulation operations & and ^ for (len = value.Length; len > 0 && value[len - 1] == 0; len--); if (len == 0) @@ -516,7 +516,7 @@ private BigInteger(Span value) int dwordCount = value.Length; bool isNegative = dwordCount > 0 && ((value[dwordCount - 1] & 0x80000000) == 0x80000000); - // Try to conserve space as much as possible by checking for wasted leading uint[] entries + // Try to conserve space as much as possible by checking for wasted leading span entries while (dwordCount > 0 && value[dwordCount - 1] == 0) dwordCount--; if (dwordCount == 0) From 35bd53c620d1db00f68c2117105208ebd04d0802 Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 3 May 2020 18:48:31 +0300 Subject: [PATCH 61/88] Fixed code formatting issues --- .../src/System/Numerics/BigInteger.cs | 6 +++--- .../src/System/Numerics/BigIntegerCalculator.AddSub.cs | 4 ++-- .../src/System/Numerics/BigIntegerCalculator.GcdInv.cs | 2 +- .../src/System/Numerics/BigIntegerCalculator.SquMul.cs | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 6f1f5503ec73b..a2d4984310e74 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -475,7 +475,7 @@ internal BigInteger(int n, uint[]? rgu) /// /// Constructor used during bit manipulation and arithmetic. - /// When possible the uints will be packed into _sign to conserve space. + /// When possible the uints-span will be packed into _sign to conserve space. /// /// The absolute value of the number /// The bool indicating the sign of the value. @@ -483,8 +483,8 @@ internal BigInteger(ReadOnlySpan value, bool negative) { int len; - // Try to conserve space as much as possible by checking for wasted leading span entries - // sometimes the span has leading zeros from bit manipulation operations & and ^ + // Try to conserve space as much as possible by checking for wasted leading uints-span entries + // sometimes the uints-span has leading zeros from bit manipulation operations & and ^ for (len = value.Length; len > 0 && value[len - 1] == 0; len--); if (len == 0) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index fd79c9ca4eb6b..ef12aa82404de 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -44,13 +44,13 @@ public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span> 32; } - for (; i < left.Length; i++) + for ( ; i < left.Length; i++) { long digit = left[i] + carry; bits[i] = unchecked((uint)digit); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 894fd441a6e20..a3f6d32d41b93 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -219,7 +219,7 @@ private static int Overwrite(Span bits, uint value) if (bits.Length > 1) { - // Ensure leading zeros + // Ensure leading zeros in little-endian bits.Slice(1).Clear(); } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 209fe3b72329d..04c66e336d735 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -151,7 +151,7 @@ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, S // Executes different algorithms for computing z = a * b // based on the actual length of b. If b is "small" enough - // we stick to the classic "grammar-school" method; for the + // we stick to the classic "grammar-school" method; f the // rest we switch to implementations with less complexity // albeit more overhead (which needs to pay off!). @@ -279,21 +279,21 @@ private static void SubtractCore(ReadOnlySpan left, ReadOnlySpan rig int i = 0; long carry = 0L; - for (; i < right.Length; i++) + for ( ; i < right.Length; i++) { ref uint elementPtr = ref core[i]; long digit = (elementPtr + carry) - left[i] - right[i]; elementPtr = unchecked((uint)digit); carry = digit >> 32; } - for (; i < left.Length; i++) + for ( ; i < left.Length; i++) { ref uint elementPtr = ref core[i]; long digit = (elementPtr + carry) - left[i]; elementPtr = unchecked((uint)digit); carry = digit >> 32; } - for (; carry != 0 && i < core.Length; i++) + for ( ; carry != 0 && i < core.Length; i++) { ref uint elementPtr = ref core[i]; long digit = elementPtr + carry; From 9f498b0946a0b3348fdcdb122392b18016141c82 Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 30 Aug 2020 19:13:17 +0300 Subject: [PATCH 62/88] Share StackAllocThreshold const --- .../src/System/Numerics/BigInteger.cs | 75 +++++++++---------- .../Numerics/BigIntegerCalculator.DivRem.cs | 2 +- .../Numerics/BigIntegerCalculator.GcdInv.cs | 2 +- .../Numerics/BigIntegerCalculator.PowMod.cs | 35 +++++---- .../Numerics/BigIntegerCalculator.SquMul.cs | 24 ++++-- .../Numerics/BigIntegerCalculator.Utils.cs | 7 +- .../tests/BigInteger/multiply.cs | 2 +- 7 files changed, 83 insertions(+), 64 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 4725773bf5160..d6d95a33fb058 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -13,7 +13,6 @@ namespace System.Numerics [System.Runtime.CompilerServices.TypeForwardedFrom("System.Numerics, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")] public readonly struct BigInteger : IFormattable, IComparable, IComparable, IEquatable { - private const int StackAllocThreshold = 256; private const uint kuMaskHighBit = unchecked((uint)int.MinValue); private const int kcbitUint = 32; private const int kcbitUlong = 64; @@ -741,7 +740,7 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big uint[]? bitsFromPool = null; int size = dividend._bits.Length; - Span quotient = size <= StackAllocThreshold ? + Span quotient = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -771,13 +770,13 @@ stackalloc uint[size] { uint[]? remainderFromPool = null; int size = dividend._bits.Length; - Span rest = size <= StackAllocThreshold ? + Span rest = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (remainderFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); uint[]? quotientFromPool = null; size = dividend._bits.Length - divisor._bits.Length + 1; - Span quotient = size <= StackAllocThreshold ? + Span quotient = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -894,7 +893,7 @@ private static BigInteger GreatestCommonDivisor(ReadOnlySpan leftBits, Rea } else if (rightBits.Length == 2) { - Span bits = leftBits.Length <= StackAllocThreshold ? + Span bits = leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[leftBits.Length] : (bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).AsSpan(0, leftBits.Length); @@ -907,7 +906,7 @@ stackalloc uint[leftBits.Length] } else { - Span bits = leftBits.Length <= StackAllocThreshold ? + Span bits = leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[leftBits.Length] : (bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).AsSpan(0, leftBits.Length); @@ -963,7 +962,7 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege { int size = (modulus._bits?.Length ?? 1) << 1; uint[]? bitsFromPool = null; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); bits.Clear(); @@ -1021,7 +1020,7 @@ public static BigInteger Pow(BigInteger value, int exponent) return value; int size = BigIntegerCalculator.PowBound(power, 1); - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); bits.Clear(); @@ -1032,7 +1031,7 @@ stackalloc uint[size] else { int size = BigIntegerCalculator.PowBound(power, value._bits!.Length); - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); bits.Clear(); @@ -1585,7 +1584,7 @@ private static BigInteger Add(ReadOnlySpan leftBits, int leftSign, ReadOnl Debug.Assert(!rightBits.IsEmpty); int size = rightBits.Length + 1; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1597,7 +1596,7 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty); int size = leftBits.Length + 1; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1609,7 +1608,7 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); int size = rightBits.Length + 1; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1621,7 +1620,7 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); int size = leftBits.Length + 1; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1663,7 +1662,7 @@ private static BigInteger Subtract(ReadOnlySpan leftBits, int leftSign, Re Debug.Assert(!rightBits.IsEmpty); int size = rightBits.Length; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1675,7 +1674,7 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty); int size = leftBits.Length; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1685,7 +1684,7 @@ stackalloc uint[size] else if (BigIntegerCalculator.Compare(leftBits, rightBits) < 0) { int size = rightBits.Length; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1697,7 +1696,7 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); int size = leftBits.Length; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -1969,21 +1968,21 @@ public static explicit operator decimal(BigInteger value) uint[]? leftBufferFromPool = null; int size = (left._bits?.Length ?? 1) + 1; - Span x = size <= StackAllocThreshold ? + Span x = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : leftBufferFromPool = ArrayPool.Shared.Rent(size); x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; - Span y = size <= StackAllocThreshold ? + Span y = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); - Span z = size <= StackAllocThreshold ? + Span z = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -2025,21 +2024,21 @@ stackalloc uint[size] uint[]? leftBufferFromPool = null; int size = (left._bits?.Length ?? 1) + 1; - Span x = size <= StackAllocThreshold ? + Span x = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : leftBufferFromPool = ArrayPool.Shared.Rent(size); x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; - Span y = size <= StackAllocThreshold ? + Span y = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); - Span z = size <= StackAllocThreshold ? + Span z = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -2076,21 +2075,21 @@ stackalloc uint[size] uint[]? leftBufferFromPool = null; int size = (left._bits?.Length ?? 1) + 1; - Span x = size <= StackAllocThreshold ? + Span x = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : leftBufferFromPool = ArrayPool.Shared.Rent(size); x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; - Span y = size <= StackAllocThreshold ? + Span y = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : rightBufferFromPool = ArrayPool.Shared.Rent(size); y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); - Span z = size <= StackAllocThreshold ? + Span z = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -2126,14 +2125,14 @@ stackalloc uint[size] uint[]? xdFromPool = null; int xl = value._bits?.Length ?? 1; - Span xd = xl <= StackAllocThreshold ? + Span xd = xl <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[xl] : (xdFromPool = ArrayPool.Shared.Rent(xl)).AsSpan(0, xl); bool negx = value.GetPartsForBitManipulation(xd); int zl = xl + digitShift + 1; uint[]? zdFromPool = null; - Span zd = zl <= StackAllocThreshold ? + Span zd = zl <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[zl] : (zdFromPool = ArrayPool.Shared.Rent(zl)).AsSpan(0, zl); zd.Clear(); @@ -2182,7 +2181,7 @@ stackalloc uint[zl] uint[]? xdFromPool = null; int xl = value._bits?.Length ?? 1; - Span xd = xl <= StackAllocThreshold ? + Span xd = xl <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[xl] : (xdFromPool = ArrayPool.Shared.Rent(xl)).AsSpan(0, xl); @@ -2200,7 +2199,7 @@ stackalloc uint[xl] uint[]? zdFromPool = null; int zl = Math.Max(xl - digitShift, 0); - Span zd = zl <= StackAllocThreshold ? + Span zd = zl <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[zl] : (zdFromPool = ArrayPool.Shared.Rent(zl)).AsSpan(0, zl); zd.Clear(); @@ -2305,7 +2304,7 @@ private static BigInteger Multiply(ReadOnlySpan left, int leftSign, ReadOn Debug.Assert(!right.IsEmpty); int size = right.Length + 1; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -2317,7 +2316,7 @@ stackalloc uint[size] Debug.Assert(!left.IsEmpty); int size = left.Length + 1; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -2327,7 +2326,7 @@ stackalloc uint[size] else if (left.Length == right.Length && Unsafe.AreSame(ref Unsafe.AsRef(in left[0]), ref Unsafe.AsRef(in right[0]))) { int size = left.Length + right.Length; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -2339,7 +2338,7 @@ stackalloc uint[size] Debug.Assert(!left.IsEmpty && !right.IsEmpty); int size = left.Length + right.Length; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); bits.Clear(); @@ -2352,7 +2351,7 @@ stackalloc uint[size] Debug.Assert(!left.IsEmpty && !right.IsEmpty); int size = left.Length + right.Length; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); bits.Clear(); @@ -2394,7 +2393,7 @@ stackalloc uint[size] Debug.Assert(dividend._bits != null); int size = dividend._bits.Length; - Span quotient = size <= StackAllocThreshold ? + Span quotient = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -2420,7 +2419,7 @@ stackalloc uint[size] else { int size = dividend._bits.Length - divisor._bits.Length + 1; - Span quotient = size < StackAllocThreshold ? + Span quotient = size < BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); @@ -2470,7 +2469,7 @@ stackalloc uint[size] uint[]? bitsFromPool = null; int size = dividend._bits.Length; - Span bits = size <= StackAllocThreshold ? + Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index f072fe1ab63f2..119a01ae0694f 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -86,7 +86,7 @@ public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Spa // NOTE: left will get overwritten, we need a local copy // However, mutated left is not used afterwards, so use array pooling or stack alloc - Span leftCopy = left.Length <= AllocationThreshold ? + Span leftCopy = left.Length <= StackAllocThreshold ? stackalloc uint[left.Length] : (leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).AsSpan(0, left.Length); left.CopyTo(leftCopy); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index cae03c1f78a89..232cb916a930b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -64,7 +64,7 @@ public static void Gcd(ReadOnlySpan left, ReadOnlySpan right, Span rightCopy = right.Length <= AllocationThreshold ? + Span rightCopy = right.Length <= StackAllocThreshold ? stackalloc uint[right.Length] : (rightCopyFromPool = ArrayPool.Shared.Rent(right.Length)).AsSpan(0, right.Length); right.CopyTo(rightCopy); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index 68631938e5d6c..aec42c1a6b3a9 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -24,13 +24,13 @@ public static void Pow(ReadOnlySpan value, uint power, Span bits) Debug.Assert(bits.Length == PowBound(power, value.Length)); uint[]? tempFromPool = null; - Span temp = bits.Length <= AllocationThreshold ? + Span temp = bits.Length <= StackAllocThreshold ? stackalloc uint[bits.Length] : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); temp.Clear(); uint[]? valueCopyFromPool = null; - Span valueCopy = bits.Length <= AllocationThreshold ? + Span valueCopy = bits.Length <= StackAllocThreshold ? stackalloc uint[bits.Length] : (valueCopyFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); value.CopyTo(valueCopy); @@ -215,7 +215,7 @@ public static void Pow(ReadOnlySpan value, uint power, uint[]? valueCopyFromPool = null; int size = Math.Max(value.Length, bits.Length); - Span valueCopy = size <= AllocationThreshold ? + Span valueCopy = size <= StackAllocThreshold ? stackalloc uint[size] : (valueCopyFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); valueCopy.Clear(); @@ -230,7 +230,7 @@ stackalloc uint[size] } uint[]? tempFromPool = null; - Span temp = bits.Length <= AllocationThreshold ? + Span temp = bits.Length <= StackAllocThreshold ? stackalloc uint[bits.Length] : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); temp.Clear(); @@ -260,7 +260,7 @@ public static void Pow(ReadOnlySpan value, ReadOnlySpan power, int size = Math.Max(value.Length, bits.Length); uint[]? valueCopyFromPool = null; - Span valueCopy = size <= AllocationThreshold ? + Span valueCopy = size <= StackAllocThreshold ? stackalloc uint[size] : (valueCopyFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); valueCopy.Clear(); @@ -275,7 +275,7 @@ stackalloc uint[size] } uint[]? tempFromPool = null; - Span temp = bits.Length <= AllocationThreshold ? + Span temp = bits.Length <= StackAllocThreshold ? stackalloc uint[bits.Length] : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); temp.Clear(); @@ -288,8 +288,13 @@ stackalloc uint[bits.Length] ArrayPool.Shared.Return(tempFromPool); } +#if DEBUG // Mutable for unit testing... - private static int ReducerThreshold = 32; + private static +#else + private const +#endif + int ReducerThreshold = 32; private static void PowCore(Span value, int valueLength, ReadOnlySpan power, ReadOnlySpan modulus, @@ -307,27 +312,27 @@ private static void PowCore(Span value, int valueLength, { int size = modulus.Length * 2 + 1; uint[]? rFromPool = null; - Span r = size <= AllocationThreshold ? + Span r = size <= StackAllocThreshold ? stackalloc uint[size] : (rFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); r.Clear(); size = r.Length - modulus.Length + 1; uint[]? muFromPool = null; - Span mu = size <= AllocationThreshold ? + Span mu = size <= StackAllocThreshold ? stackalloc uint[size] : (muFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); mu.Clear(); size = modulus.Length * 2 + 2; uint[]? q1FromPool = null; - Span q1 = size <= AllocationThreshold ? + Span q1 = size <= StackAllocThreshold ? stackalloc uint[size] : (q1FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); q1.Clear(); uint[]? q2FromPool = null; - Span q2 = size <= AllocationThreshold ? + Span q2 = size <= StackAllocThreshold ? stackalloc uint[size] : (q2FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); q2.Clear(); @@ -363,27 +368,27 @@ private static void PowCore(Span value, int valueLength, { int size = modulus.Length * 2 + 1; uint[]? rFromPool = null; - Span r = size <= AllocationThreshold ? + Span r = size <= StackAllocThreshold ? stackalloc uint[size] : (rFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); r.Clear(); size = r.Length - modulus.Length + 1; uint[]? muFromPool = null; - Span mu = size <= AllocationThreshold ? + Span mu = size <= StackAllocThreshold ? stackalloc uint[size] : (muFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); mu.Clear(); size = modulus.Length * 2 + 2; uint[]? q1FromPool = null; - Span q1 = size <= AllocationThreshold ? + Span q1 = size <= StackAllocThreshold ? stackalloc uint[size] : (q1FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); q1.Clear(); uint[]? q2FromPool = null; - Span q2 = size <= AllocationThreshold ? + Span q2 = size <= StackAllocThreshold ? stackalloc uint[size] : (q2FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); q2.Clear(); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 8d6adf4e9f907..6c8e14acc37ba 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -8,8 +8,13 @@ namespace System.Numerics { internal static partial class BigIntegerCalculator { +#if DEBUG // Mutable for unit testing... - private static int SquareThreshold = 32; + private static +#else + private const +#endif + int SquareThreshold = 32; public static void Square(ReadOnlySpan value, Span bits) { @@ -86,14 +91,14 @@ public static void Square(ReadOnlySpan value, Span bits) int foldLength = valueHigh.Length + 1; uint[]? foldFromPool = null; - Span fold = foldLength <= AllocationThreshold ? + Span fold = foldLength <= StackAllocThreshold ? stackalloc uint[foldLength] : (foldFromPool = ArrayPool.Shared.Rent(foldLength)).AsSpan(0, foldLength); fold.Clear(); int coreLength = foldLength + foldLength; uint[]? coreFromPool = null; - Span core = coreLength <= AllocationThreshold ? + Span core = coreLength <= StackAllocThreshold ? stackalloc uint[coreLength] : (coreFromPool = ArrayPool.Shared.Rent(coreLength)).AsSpan(0, coreLength); core.Clear(); @@ -138,8 +143,13 @@ public static void Multiply(ReadOnlySpan left, uint right, Span bits bits[i] = (uint)carry; } +#if DEBUG // Mutable for unit testing... - private static int MultiplyThreshold = 32; + private static +#else + private const +#endif + int MultiplyThreshold = 32; public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Span bits) { @@ -216,21 +226,21 @@ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, S int leftFoldLength = leftHigh.Length + 1; uint[]? leftFoldFromPool = null; - Span leftFold = leftFoldLength <= AllocationThreshold ? + Span leftFold = leftFoldLength <= StackAllocThreshold ? stackalloc uint[leftFoldLength] : (leftFoldFromPool = ArrayPool.Shared.Rent(leftFoldLength)).AsSpan(0, leftFoldLength); leftFold.Clear(); int rightFoldLength = rightHigh.Length + 1; uint[]? rightFoldFromPool = null; - Span rightFold = rightFoldLength <= AllocationThreshold ? + Span rightFold = rightFoldLength <= StackAllocThreshold ? stackalloc uint[rightFoldLength] : (rightFoldFromPool = ArrayPool.Shared.Rent(rightFoldLength)).AsSpan(0, rightFoldLength); rightFold.Clear(); int coreLength = leftFoldLength + rightFoldLength; uint[]? coreFromPool = null; - Span core = coreLength <= AllocationThreshold ? + Span core = coreLength <= StackAllocThreshold ? stackalloc uint[coreLength] : (coreFromPool = ArrayPool.Shared.Rent(coreLength)).AsSpan(0, coreLength); core.Clear(); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs index c28d8534203ac..d866fd424d129 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -6,8 +6,13 @@ namespace System.Numerics { internal static partial class BigIntegerCalculator { +#if DEBUG // Mutable for unit testing... - private static int AllocationThreshold = 256; + internal static +#else + internal const +#endif + int StackAllocThreshold = 256; public static int Compare(ReadOnlySpan left, ReadOnlySpan right) { diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/multiply.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/multiply.cs index 72be36c581923..a2646d5bd6cb8 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/multiply.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/multiply.cs @@ -44,7 +44,7 @@ public static void RunMultiply_TwoLargeBigIntegers_Threshold() // Again, with lower threshold BigIntTools.Utils.RunWithFakeThreshold("SquareThreshold", 8, () => BigIntTools.Utils.RunWithFakeThreshold("MultiplyThreshold", 8, () => - BigIntTools.Utils.RunWithFakeThreshold("AllocationThreshold", 8, RunMultiply_TwoLargeBigIntegers) + BigIntTools.Utils.RunWithFakeThreshold("StackAllocThreshold", 8, RunMultiply_TwoLargeBigIntegers) ) ); } From 7bc63c89b404493c82d54439a379628d4f9d9f32 Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 30 Aug 2020 20:51:03 +0300 Subject: [PATCH 63/88] Ignore const threshold values in Release config --- .../System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs index 519b1d9e5b397..27c0f5cf45c09 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs @@ -59,6 +59,9 @@ public static void RunWithFakeThreshold(string name, int value, Action action) return; // Internal frame types are not reflectable on AoT platforms. Skip the test. FieldInfo field = internalCalculator.GetDeclaredField(name); + if (field.IsLiteral) + return; // in Release config the field may be const + int lastValue = (int)field.GetValue(null); field.SetValue(null, value); try From dd7bdef55673ca85183b374b0436ac5aec339eba Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 30 Aug 2020 20:53:00 +0300 Subject: [PATCH 64/88] Literal field cannot be discovered with TypeInfo.GetDeclaredField --- .../System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs index 27c0f5cf45c09..33d6614483717 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs @@ -59,7 +59,7 @@ public static void RunWithFakeThreshold(string name, int value, Action action) return; // Internal frame types are not reflectable on AoT platforms. Skip the test. FieldInfo field = internalCalculator.GetDeclaredField(name); - if (field.IsLiteral) + if (field is null || field.IsLiteral) return; // in Release config the field may be const int lastValue = (int)field.GetValue(null); From fe53099838062d27cf18b564cebc9049c6fbce06 Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 30 Aug 2020 22:09:13 +0300 Subject: [PATCH 65/88] Removed invalid assertion --- .../src/System/Numerics/BigIntegerCalculator.FastReducer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index d750be61a6d8f..0d518fff82e41 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -105,7 +105,6 @@ private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Spa private static int SubMod(Span left, ReadOnlySpan right, ReadOnlySpan modulus, int k) { Debug.Assert(!left.IsEmpty); - Debug.Assert(!right.IsEmpty); // Executes the subtraction algorithm for left and right, // but considers only the first k limbs, which is equivalent to From 55d949fce54adfdebe73e3952421eb5b3e01f66c Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 30 Aug 2020 22:10:21 +0300 Subject: [PATCH 66/88] Removed invalid assertion --- .../src/System/Numerics/BigIntegerCalculator.FastReducer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 0d518fff82e41..547010a7148d8 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -67,7 +67,6 @@ public int Reduce(Span value) private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Span bits, int k) { - Debug.Assert(!left.IsEmpty); Debug.Assert(!right.IsEmpty); Debug.Assert(!bits.IsEmpty); Debug.Assert(bits.Length + k >= left.Length + right.Length); From 61d470d9481ea0ac6550eed2f4aa0ccf3d1f8d31 Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 30 Aug 2020 22:12:49 +0300 Subject: [PATCH 67/88] Removed invalid assertion --- .../src/System/Numerics/BigIntegerCalculator.FastReducer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 547010a7148d8..876f88e310062 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -103,8 +103,6 @@ private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Spa private static int SubMod(Span left, ReadOnlySpan right, ReadOnlySpan modulus, int k) { - Debug.Assert(!left.IsEmpty); - // Executes the subtraction algorithm for left and right, // but considers only the first k limbs, which is equivalent to // preceding reduction by 2^(32*k). Furthermore, if left is From 808c12c8a184578aedddff5b826f9e2383c7f68f Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 15 Dec 2020 11:00:12 +0200 Subject: [PATCH 68/88] Code review feedback --- .../src/System/Numerics/BigInteger.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index d6d95a33fb058..d60eca8ed80fa 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -473,7 +473,7 @@ internal BigInteger(int n, uint[]? rgu) /// /// Constructor used during bit manipulation and arithmetic. - /// When possible the uints-span will be packed into _sign to conserve space. + /// When possible the value will be packed into _sign to conserve space. /// /// The absolute value of the number /// The bool indicating the sign of the value. @@ -481,8 +481,8 @@ internal BigInteger(ReadOnlySpan value, bool negative) { int len; - // Try to conserve space as much as possible by checking for wasted leading uints-span entries - // sometimes the uints-span has leading zeros from bit manipulation operations & and ^ + // Try to conserve space as much as possible by checking for wasted leading span entries + // sometimes the span has leading zeros from bit manipulation operations & and ^ for (len = value.Length; len > 0 && value[len - 1] == 0; len--); if (len == 0) From 7db731cd9cb4d3c186c59e4f475891a77106f610 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 15 Dec 2020 11:10:57 +0200 Subject: [PATCH 69/88] Fixed header --- .../src/System/Numerics/BigIntegerCalculator.Utils.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs index d866fd424d129..1e054e2cb1f5d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -1,6 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. namespace System.Numerics { From 24d2ff4f98b0604676fa74d2bb700db52fd003fc Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 16 Jan 2021 15:41:37 +0200 Subject: [PATCH 70/88] Fixed overflow of result buffer --- .../src/System/Numerics/BigIntegerCalculator.PowMod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index aec42c1a6b3a9..207c551ec5ff0 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -465,7 +465,7 @@ private static Span PowCore(Span value, int valueLength, power = power >> 1; } - return result; + return result.Slice(0, resultLength); } private static Span PowCore(Span value, int valueLength, From 60777dd7978f1416f1539f5f341d3db40c5b1e0a Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 16 Jan 2021 19:33:28 +0200 Subject: [PATCH 71/88] Reduced ctor visibility --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index d60eca8ed80fa..eb474e646aefb 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -477,7 +477,7 @@ internal BigInteger(int n, uint[]? rgu) /// /// The absolute value of the number /// The bool indicating the sign of the value. - internal BigInteger(ReadOnlySpan value, bool negative) + private BigInteger(ReadOnlySpan value, bool negative) { int len; From fa5eb5efa3d94b57e12d606a8a3bf06537557de5 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 18 Jan 2021 21:36:18 +0200 Subject: [PATCH 72/88] Eliminated bounds check --- .../Numerics/BigIntegerCalculator.AddSub.cs | 87 +++++++++++-------- .../Numerics/BigIntegerCalculator.DivRem.cs | 26 ++++-- .../Numerics/BigIntegerCalculator.SquMul.cs | 53 +++++++---- 3 files changed, 105 insertions(+), 61 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index 0db86632897fd..b71b8592ab507 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Numerics { @@ -35,27 +37,32 @@ public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span= right.Length); Debug.Assert(bits.Length == left.Length + 1); + int i = 0; + long carry = 0L; + + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); + // Executes the "grammar-school" algorithm for computing z = a + b. // While calculating z_i = a_i + b_i we take care of overflow: // Since a_i + b_i + c <= 2(2^32 - 1) + 1 = 2^33 - 1, our carry c // has always the value 1 or 0; hence, we're safe here. - int i = 0; - long carry = 0L; - for ( ; i < right.Length; i++) { - long digit = (left[i] + carry) + right[i]; - bits[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) + right[i]; + Unsafe.Add(ref resultPtr, i) = unchecked((uint)digit); carry = digit >> 32; } for ( ; i < left.Length; i++) { long digit = left[i] + carry; - bits[i] = unchecked((uint)digit); + Unsafe.Add(ref resultPtr, i) = unchecked((uint)digit); carry = digit >> 32; } - bits[i] = (uint)carry; + Unsafe.Add(ref resultPtr, i) = (uint)carry; } private static void AddSelf(Span left, ReadOnlySpan right) @@ -64,24 +71,28 @@ private static void AddSelf(Span left, ReadOnlySpan right) Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); + int i = 0; + long carry = 0L; + + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint rightPtr = ref MemoryMarshal.GetReference(right); + // Executes the "grammar-school" algorithm for computing z = a + b. // Same as above, but we're writing the result directly to a and // stop execution, if we're out of b and c is already 0. - int i = 0; - long carry = 0L; - for (; i < right.Length; i++) + for ( ; i < right.Length; i++) { - ref uint leftElement = ref left[i]; - long digit = (leftElement + carry) + right[i]; - leftElement = unchecked((uint)digit); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) + right[i]; + Unsafe.Add(ref leftPtr, i) = unchecked((uint)digit); carry = digit >> 32; } - for (; carry != 0 && i < left.Length; i++) + for ( ; carry != 0 && i < left.Length; i++) { - ref uint leftElement = ref left[i]; - long digit = leftElement + carry; - leftElement = (uint)digit; + long digit = Unsafe.Add(ref leftPtr, i) + carry; + Unsafe.Add(ref leftPtr, i) = (uint)digit; carry = digit >> 32; } @@ -116,24 +127,29 @@ public static void Subtract(ReadOnlySpan left, ReadOnlySpan right, S Debug.Assert(Compare(left, right) >= 0); Debug.Assert(bits.Length == left.Length); + int i = 0; + long carry = 0L; + + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); + // Executes the "grammar-school" algorithm for computing z = a - b. // While calculating z_i = a_i - b_i we take care of overflow: // Since a_i - b_i doesn't need any additional bit, our carry c // has always the value -1 or 0; hence, we're safe here. - int i = 0; - long carry = 0L; - - for (; i < right.Length; i++) + for ( ; i < right.Length; i++) { - long digit = (left[i] + carry) - right[i]; - bits[i] = unchecked((uint)digit); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) - right[i]; + Unsafe.Add(ref resultPtr, i) = unchecked((uint)digit); carry = digit >> 32; } - for (; i < left.Length; i++) + for ( ; i < left.Length; i++) { long digit = left[i] + carry; - bits[i] = (uint)digit; + Unsafe.Add(ref resultPtr, i) = (uint)digit; carry = digit >> 32; } @@ -147,25 +163,28 @@ private static void SubtractSelf(Span left, ReadOnlySpan right) Debug.Assert(left.Length >= right.Length); Debug.Assert(Compare(left, right) >= 0); + int i = 0; + long carry = 0L; + + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint rightPtr = ref MemoryMarshal.GetReference(right); + // Executes the "grammar-school" algorithm for computing z = a - b. // Same as above, but we're writing the result directly to a and // stop execution, if we're out of b and c is already 0. - int i = 0; - long carry = 0L; - for (; i < right.Length; i++) { - ref uint leftElement = ref left[i]; - long digit = (leftElement + carry) - right[i]; - leftElement = unchecked((uint)digit); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) - Unsafe.Add(ref rightPtr, i); + Unsafe.Add(ref leftPtr, i) = unchecked((uint)digit); carry = digit >> 32; } for (; carry != 0 && i < left.Length; i++) { - ref uint leftElement = ref left[i]; - long digit = leftElement + carry; - leftElement = (uint)digit; + long digit = Unsafe.Add(ref leftPtr, i) + carry; + Unsafe.Add(ref leftPtr, i) = (uint)digit; carry = digit >> 32; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 119a01ae0694f..c89a2a113e056 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -3,6 +3,8 @@ using System.Buffers; using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Numerics { @@ -118,13 +120,19 @@ private static void Divide(Span left, ReadOnlySpan right, Span Debug.Assert(bits.Length == left.Length - right.Length + 1 || bits.Length == 0); + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint rightPtr = ref MemoryMarshal.GetReference(right); + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); + // Executes the "grammar-school" algorithm for computing q = a / b. // Before calculating q_i, we get more bits into the highest bit // block of the divisor. Thus, guessing digits of the quotient // will be more precise. Additionally we'll get r = a % b. - uint divHi = right[right.Length - 1]; - uint divLo = right.Length > 1 ? right[right.Length - 2] : 0; + uint divHi = Unsafe.Add(ref rightPtr, right.Length - 1); + uint divLo = right.Length > 1 ? Unsafe.Add(ref rightPtr, right.Length - 2) : 0; // We measure the leading zeros of the divisor int shift = BitOperations.LeadingZeroCount(divHi); @@ -133,7 +141,7 @@ private static void Divide(Span left, ReadOnlySpan right, Span // And, we make sure the most significant bit is set if (shift > 0) { - uint divNx = right.Length > 2 ? right[right.Length - 3] : 0; + uint divNx = right.Length > 2 ? Unsafe.Add(ref rightPtr, right.Length - 3) : 0; divHi = (divHi << shift) | (divLo >> backShift); divLo = (divLo << shift) | (divNx >> backShift); @@ -144,15 +152,15 @@ private static void Divide(Span left, ReadOnlySpan right, Span for (int i = left.Length; i >= right.Length; i--) { int n = i - right.Length; - uint t = (uint)i < (uint)left.Length ? left[i] : 0; + uint t = (uint)i < (uint)left.Length ? Unsafe.Add(ref leftPtr, i) : 0; - ulong valHi = ((ulong)t << 32) | left[i - 1]; - uint valLo = i > 1 ? left[i - 2] : 0; + ulong valHi = ((ulong)t << 32) | Unsafe.Add(ref leftPtr, i - 1); + uint valLo = i > 1 ? Unsafe.Add(ref leftPtr, i - 2) : 0; // We shifted the divisor, we shift the dividend too if (shift > 0) { - uint valNx = i > 2 ? left[i - 3] : 0; + uint valNx = i > 2 ? Unsafe.Add(ref leftPtr, i - 3) : 0; valHi = (valHi << shift) | (valLo >> backShift); valLo = (valLo << shift) | (valNx >> backShift); @@ -186,9 +194,9 @@ private static void Divide(Span left, ReadOnlySpan right, Span // We have the digit! if (!bits.IsEmpty) - bits[n] = (uint)digit; + Unsafe.Add(ref resultPtr, n) = (uint)digit; if ((uint)i < (uint)left.Length) - left[i] = 0; + Unsafe.Add(ref leftPtr, i) = 0; } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 6c8e14acc37ba..844ce06ed391a 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -3,6 +3,8 @@ using System.Buffers; using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Numerics { @@ -32,6 +34,11 @@ public static void Square(ReadOnlySpan value, Span bits) if (value.Length < SquareThreshold) { + // Switching to managed references helps eliminating + // index bounds check... + ref uint valuePtr = ref MemoryMarshal.GetReference(value); + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); + // Squares the bits using the "grammar-school" method. // Envisioning the "rhombus" of a pen-and-paper calculation // we see that computing z_i+j += a_j * a_i can be optimized @@ -46,16 +53,17 @@ public static void Square(ReadOnlySpan value, Span bits) for (int i = 0; i < value.Length; i++) { ulong carry = 0UL; + uint v = Unsafe.Add(ref valuePtr, i); for (int j = 0; j < i; j++) { - ulong digit1 = bits[i + j] + carry; - ulong digit2 = (ulong)value[j] * value[i]; - bits[i + j] = unchecked((uint)(digit1 + (digit2 << 1))); + ulong digit1 = Unsafe.Add(ref resultPtr, i + j) + carry; + ulong digit2 = (ulong)Unsafe.Add(ref valuePtr, j) * v; + Unsafe.Add(ref resultPtr, i + j) = unchecked((uint)(digit1 + (digit2 << 1))); carry = (digit2 + (digit1 >> 1)) >> 31; } - ulong digits = (ulong)value[i] * value[i] + carry; - bits[i + i] = unchecked((uint)digits); - bits[i + i + 1] = (uint)(digits >> 32); + ulong digits = (ulong)v * v + carry; + Unsafe.Add(ref resultPtr, i + i) = unchecked((uint)digits); + Unsafe.Add(ref resultPtr, i + i + 1) = (uint)(digits >> 32); } } else @@ -169,6 +177,12 @@ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, S if (right.Length < MultiplyThreshold) { + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint rightPtr = ref MemoryMarshal.GetReference(right); + ref uint resultPtr = ref MemoryMarshal.GetReference(bits); + // Multiplies the bits using the "grammar-school" method. // Envisioning the "rhombus" of a pen-and-paper calculation // should help getting the idea of these two loops... @@ -181,12 +195,12 @@ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, S ulong carry = 0UL; for (int j = 0; j < left.Length; j++) { - ref uint elementPtr = ref bits[i + j]; - ulong digits = elementPtr + carry + (ulong)left[j] * right[i]; + ref uint elementPtr = ref Unsafe.Add(ref resultPtr, i + j); + ulong digits = elementPtr + carry + (ulong)Unsafe.Add(ref leftPtr, j) * Unsafe.Add(ref rightPtr, i); elementPtr = unchecked((uint)digits); carry = digits >> 32; } - bits[i + left.Length] = (uint)carry; + Unsafe.Add(ref resultPtr, i + left.Length) = (uint)carry; } } else @@ -288,25 +302,28 @@ private static void SubtractCore(ReadOnlySpan left, ReadOnlySpan rig int i = 0; long carry = 0L; + // Switching to managed references helps eliminating + // index bounds check... + ref uint leftPtr = ref MemoryMarshal.GetReference(left); + ref uint rightPtr = ref MemoryMarshal.GetReference(right); + ref uint corePtr = ref MemoryMarshal.GetReference(core); + for ( ; i < right.Length; i++) { - ref uint elementPtr = ref core[i]; - long digit = (elementPtr + carry) - left[i] - right[i]; - elementPtr = unchecked((uint)digit); + long digit = (Unsafe.Add(ref corePtr, i) + carry) - Unsafe.Add(ref leftPtr, i) - Unsafe.Add(ref rightPtr, i); + Unsafe.Add(ref corePtr, i) = unchecked((uint)digit); carry = digit >> 32; } for ( ; i < left.Length; i++) { - ref uint elementPtr = ref core[i]; - long digit = (elementPtr + carry) - left[i]; - elementPtr = unchecked((uint)digit); + long digit = (Unsafe.Add(ref corePtr, i) + carry) - Unsafe.Add(ref leftPtr, i); + Unsafe.Add(ref corePtr, i) = unchecked((uint)digit); carry = digit >> 32; } for ( ; carry != 0 && i < core.Length; i++) { - ref uint elementPtr = ref core[i]; - long digit = elementPtr + carry; - elementPtr = (uint)digit; + long digit = Unsafe.Add(ref corePtr, i) + carry; + Unsafe.Add(ref corePtr, i) = (uint)digit; carry = digit >> 32; } } From ec1285dd5e9e5d73a377b725a48f67975cda9246 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 20 Jan 2021 20:51:25 +0200 Subject: [PATCH 73/88] Returned optimization using Math.DivRem --- .../src/System/Numerics/BigInteger.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index eb474e646aefb..761e90fc28ba7 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -720,8 +720,9 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big if (trivialDividend && trivialDivisor) { - remainder = dividend._sign % divisor._sign; - return dividend._sign / divisor._sign; + BigInteger quotient; + (quotient, remainder) = Math.DivRem(dividend._sign, divisor._sign); + return quotient; } if (trivialDividend) From f01d08976dd99ae95e4b13a80f7b2b1ccf5135fe Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 20 Jan 2021 21:10:59 +0200 Subject: [PATCH 74/88] Added braces to be consistent with other branches --- .../src/System/Numerics/BigInteger.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 761e90fc28ba7..81c53e89928da 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -486,11 +486,13 @@ private BigInteger(ReadOnlySpan value, bool negative) for (len = value.Length; len > 0 && value[len - 1] == 0; len--); if (len == 0) + { this = s_bnZeroInt; - // Values like (Int32.MaxValue+1) are stored as "0x80000000" and as such cannot be packed into _sign + } else if (len == 1 && value[0] < kuMaskHighBit) { - _sign = (negative ? -(int)value[0] : (int)value[0]); + // Values like (Int32.MaxValue+1) are stored as "0x80000000" and as such cannot be packed into _sign + _sign = negative ? -(int)value[0] : (int)value[0]; _bits = null; // Although Int32.MinValue fits in _sign, we represent this case differently for negate if (_sign == int.MinValue) From 484d6ff82774fa058509cbc2062fff95d1c9d3a4 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 20 Jan 2021 21:12:44 +0200 Subject: [PATCH 75/88] Fixed location of the comment --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 81c53e89928da..17d719a9aa796 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -494,9 +494,11 @@ private BigInteger(ReadOnlySpan value, bool negative) // Values like (Int32.MaxValue+1) are stored as "0x80000000" and as such cannot be packed into _sign _sign = negative ? -(int)value[0] : (int)value[0]; _bits = null; - // Although Int32.MinValue fits in _sign, we represent this case differently for negate if (_sign == int.MinValue) + { + // Although Int32.MinValue fits in _sign, we represent this case differently for negate this = s_bnMinInt; + } } else { From e1161f02ee4365ef1cc6438d0b3980b6e12c6908 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 20 Jan 2021 21:14:27 +0200 Subject: [PATCH 76/88] Combine slice and conversion as the single line --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 17d719a9aa796..0803eea7923e7 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -503,8 +503,7 @@ private BigInteger(ReadOnlySpan value, bool negative) else { _sign = negative ? -1 : +1; - value = value.Slice(0, len); - _bits = value.ToArray(); + _bits = value.Slice(0, len).ToArray(); } AssertValid(); } From 9855da5fbdc8e456b543b79c24173945c9c4b649 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 20 Jan 2021 21:15:23 +0200 Subject: [PATCH 77/88] Combine slice and conversion as the single line --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 0803eea7923e7..a023e798ea6e5 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -586,8 +586,7 @@ private BigInteger(Span value) else { _sign = -1; - value = value.Slice(0, len); - _bits = value.ToArray(); + _bits = value.Slice(0, len).ToArray(); } AssertValid(); return; From eeb2cdd56c9fd6d2dd973b007d3d0041911bbf32 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 10 May 2021 22:58:53 +0300 Subject: [PATCH 78/88] Merge with parsing optimizations --- .../src/System/Numerics/BigInteger.cs | 10 +++++----- .../src/System/Numerics/BigIntegerCalculator.Utils.cs | 2 +- .../src/System/Numerics/BigNumber.cs | 4 ++-- .../src/System/Numerics/NumericsHelpers.cs | 5 +++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index df70831e93886..b1a60fbec5877 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -411,7 +411,7 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE if (isNegative) { - NumericsHelpers.MakeTwosComplement(val); // Mutates val + NumericsHelpers.DangerousMakeTwosComplement(val); // Mutates val // Pack _bits to remove any wasted space after the twos complement int len = val.Length - 1; @@ -560,7 +560,7 @@ private BigInteger(Span value) } // Finally handle the more complex cases where we must transform the input into sign magnitude - NumericsHelpers.MakeTwosComplement(value); // mutates val + NumericsHelpers.DangerousMakeTwosComplement(value); // mutates val // Pack _bits to remove any wasted space after the twos complement int len = value.Length; @@ -1512,7 +1512,7 @@ private int WriteTo(Span buffer) buffer = buffer.Slice(0, _bits.Length + 1); if (_sign == -1) { - NumericsHelpers.MakeTwosComplement(buffer[..^1]); // Mutates dwords + NumericsHelpers.DangerousMakeTwosComplement(buffer[..^1]); // Mutates dwords highDWord = uint.MaxValue; } else @@ -2197,7 +2197,7 @@ stackalloc uint[xl] result = MinusOne; goto exit; } - NumericsHelpers.MakeTwosComplement(xd); // Mutates xd + NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd } uint[]? zdFromPool = null; @@ -2231,7 +2231,7 @@ stackalloc uint[zl] } if (negx) { - NumericsHelpers.MakeTwosComplement(zd); // Mutates zd + NumericsHelpers.DangerousMakeTwosComplement(zd); // Mutates zd } result = new BigInteger(zd, negx); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs index 1e054e2cb1f5d..37d4ae7596568 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs @@ -11,7 +11,7 @@ internal static #else internal const #endif - int StackAllocThreshold = 256; + int StackAllocThreshold = 64; public static int Compare(ReadOnlySpan left, ReadOnlySpan right) { diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index 957bd732fed57..2a6ad33bda277 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -412,7 +412,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt int[]? arrayFromPool = null; - Span bitsBuffer = (blockCount <= BigInteger.StackallocUInt32Limit) + Span bitsBuffer = (blockCount <= BigIntegerCalculator.StackAllocThreshold) ? stackalloc uint[blockCount] : MemoryMarshal.Cast((arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount)); @@ -496,7 +496,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt private static bool NumberToBigInteger(ref BigNumberBuffer number, out BigInteger result) { - Span stackBuffer = stackalloc uint[BigInteger.StackallocUInt32Limit]; + Span stackBuffer = stackalloc uint[BigIntegerCalculator.StackAllocThreshold]; Span currentBuffer = stackBuffer; int currentBufferSize = 0; int[]? arrayFromPool = null; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs index 9315040a32153..aba55a6e44408 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs @@ -104,8 +104,9 @@ public static double GetDoubleFromParts(int sign, int exp, ulong man) return du.dbl; } - // Do an in-place two's complement. - public static void MakeTwosComplement(Span d) + // Do an in-place two's complement. "Dangerous" because it causes + // a mutation and needs to be used with care for immutable types. + public static void DangerousMakeTwosComplement(Span d) { if (d.Length > 0) { From 1b30bb0048488462e36580b35065387a85baacbd Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 10 May 2021 23:53:20 +0300 Subject: [PATCH 79/88] Reduced checks --- .../src/System/Numerics/BigInteger.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index b1a60fbec5877..acee84c691f60 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -969,13 +969,16 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege stackalloc uint[size] : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); bits.Clear(); - if (trivialValue && trivialExponent) + if (trivialValue) { - BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), modulus._bits!, bits); - } - else if (trivialValue) - { - BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits!, modulus._bits!, bits); + if (trivialExponent) + { + BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), modulus._bits!, bits); + } + else + { + BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits!, modulus._bits!, bits); + } } else if (trivialExponent) { From fb514a1ef7e069b036fed05b95d09f0b4c0bbc8d Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 11 May 2021 00:01:30 +0300 Subject: [PATCH 80/88] Removed redundant global const --- .../src/System/Numerics/BigInteger.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index acee84c691f60..e47ecc6ffe6d8 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -17,7 +17,6 @@ namespace System.Numerics private const int kcbitUint = 32; private const int kcbitUlong = 64; private const int DecimalScaleFactorMask = 0x00FF0000; - private const int DecimalSignMask = unchecked((int)0x80000000); // For values int.MinValue < n <= int.MaxValue, the value is stored in sign // and _bits is null. For all other values, sign is +1 or -1 and the bits are in _bits @@ -213,6 +212,7 @@ public BigInteger(decimal value) Debug.Assert(bits.Length == 4 && (bits[3] & DecimalScaleFactorMask) == 0); + const int signMask = unchecked((int)kuMaskHighBit); int size = 3; while (size > 0 && bits[size - 1] == 0) size--; @@ -225,7 +225,7 @@ public BigInteger(decimal value) // bits[0] is the absolute value of this decimal // if bits[0] < 0 then it is too large to be packed into _sign _sign = bits[0]; - _sign *= ((bits[3] & DecimalSignMask) != 0) ? -1 : +1; + _sign *= ((bits[3] & signMask) != 0) ? -1 : +1; _bits = null; } else @@ -241,7 +241,7 @@ public BigInteger(decimal value) _bits[2] = (uint)bits[2]; } - _sign = ((bits[3] & DecimalSignMask) != 0) ? -1 : +1; + _sign = ((bits[3] & signMask) != 0) ? -1 : +1; } AssertValid(); } From b4ac5e5b990dc3eb1a555bc85318f054e0313ed3 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 11 May 2021 00:10:25 +0300 Subject: [PATCH 81/88] Replaced magic consts with their named equivalents --- .../System.Runtime.Numerics/src/System/Numerics/BigInteger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index e47ecc6ffe6d8..2573eb4fbb88a 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -515,7 +515,7 @@ private BigInteger(ReadOnlySpan value, bool negative) private BigInteger(Span value) { int dwordCount = value.Length; - bool isNegative = dwordCount > 0 && ((value[dwordCount - 1] & 0x80000000) == 0x80000000); + bool isNegative = dwordCount > 0 && ((value[dwordCount - 1] & kuMaskHighBit) == kuMaskHighBit); // Try to conserve space as much as possible by checking for wasted leading span entries while (dwordCount > 0 && value[dwordCount - 1] == 0) dwordCount--; From 932eef3c63eee3a483e3437118a96b0282de9650 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 12 May 2021 11:52:31 +0300 Subject: [PATCH 82/88] Removed useless asserts --- .../src/System/Numerics/BigIntegerCalculator.AddSub.cs | 8 -------- .../src/System/Numerics/BigIntegerCalculator.DivRem.cs | 4 ---- .../src/System/Numerics/BigIntegerCalculator.SquMul.cs | 6 ------ 3 files changed, 18 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index b71b8592ab507..21390ebc1e5eb 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -32,8 +32,6 @@ public static void Add(ReadOnlySpan left, uint right, Span bits) public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - Debug.Assert(left.Length >= 0); - Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); Debug.Assert(bits.Length == left.Length + 1); @@ -67,8 +65,6 @@ public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span left, ReadOnlySpan right) { - Debug.Assert(left.Length >= 0); - Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); int i = 0; @@ -121,8 +117,6 @@ public static void Subtract(ReadOnlySpan left, uint right, Span bits public static void Subtract(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - Debug.Assert(left.Length >= 0); - Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); Debug.Assert(Compare(left, right) >= 0); Debug.Assert(bits.Length == left.Length); @@ -158,8 +152,6 @@ public static void Subtract(ReadOnlySpan left, ReadOnlySpan right, S private static void SubtractSelf(Span left, ReadOnlySpan right) { - Debug.Assert(left.Length >= 0); - Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); Debug.Assert(Compare(left, right) >= 0); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index c89a2a113e056..6eac0b406108d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -202,8 +202,6 @@ private static void Divide(Span left, ReadOnlySpan right, Span private static uint AddDivisor(Span left, ReadOnlySpan right) { - Debug.Assert(left.Length >= 0); - Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); // Repairs the dividend, if the last subtract was too much @@ -223,8 +221,6 @@ private static uint AddDivisor(Span left, ReadOnlySpan right) private static uint SubtractDivisor(Span left, ReadOnlySpan right, ulong q) { - Debug.Assert(left.Length >= 0); - Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); Debug.Assert(q <= 0xFFFFFFFF); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 844ce06ed391a..e0ca57ac1054a 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -20,7 +20,6 @@ private const public static void Square(ReadOnlySpan value, Span bits) { - Debug.Assert(value.Length >= 0); Debug.Assert(bits.Length == value.Length + value.Length); // Executes different algorithms for computing z = a * a @@ -161,8 +160,6 @@ private const public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Span bits) { - Debug.Assert(left.Length >= 0); - Debug.Assert(right.Length >= 0); Debug.Assert(left.Length >= right.Length); Debug.Assert(bits.Length == left.Length + right.Length); @@ -286,9 +283,6 @@ stackalloc uint[coreLength] private static void SubtractCore(ReadOnlySpan left, ReadOnlySpan right, Span core) { - Debug.Assert(left.Length >= 0); - Debug.Assert(right.Length >= 0); - Debug.Assert(core.Length >= 0); Debug.Assert(left.Length >= right.Length); Debug.Assert(core.Length >= left.Length); From a4f3f2241ac5bbacd6623dc690600a3526941cad Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 5 Aug 2021 23:34:24 +0300 Subject: [PATCH 83/88] Manual merge with #53984 PR --- .../src/System/Numerics/BigInteger.cs | 65 ++++++++++--------- .../src/System/Numerics/Complex.cs | 3 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index ac65d2519e2aa..674f8690ff302 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Numerics { @@ -263,7 +264,7 @@ public BigInteger(ReadOnlySpan value, bool isUnsigned = false, bool isBigE bool isNegative; if (byteCount > 0) { - byte mostSignificantByte = isBigEndian ? value[0] : value[byteCount - 1]; + byte mostSignificantByte = isBigEndian ? value[0] : value[^1]; isNegative = (mostSignificantByte & 0x80) != 0 && !isUnsigned; if (mostSignificantByte == 0) @@ -605,12 +606,12 @@ public bool IsPowerOfTwo AssertValid(); if (_bits == null) - return (_sign & (_sign - 1)) == 0 && _sign != 0; + return BitOperations.IsPow2(_sign); if (_sign != 1) return false; int iu = _bits.Length - 1; - if ((_bits[iu] & (_bits[iu] - 1)) != 0) + if (!BitOperations.IsPow2(_bits[iu])) return false; while (--iu >= 0) { @@ -819,12 +820,12 @@ public static double Log(BigInteger value, double baseValue) if (value._bits == null) return Math.Log(value._sign, baseValue); - ulong h = value._bits[value._bits.Length - 1]; - ulong m = value._bits.Length > 1 ? value._bits[value._bits.Length - 2] : 0; - ulong l = value._bits.Length > 2 ? value._bits[value._bits.Length - 3] : 0; + ulong h = value._bits[^1]; + ulong m = value._bits.Length > 1 ? value._bits[^2] : 0; + ulong l = value._bits.Length > 2 ? value._bits[^3] : 0; // Measure the exact bit count - int c = NumericsHelpers.CbitHighZero((uint)h); + int c = BitOperations.LeadingZeroCount((uint)h); long b = (long)value._bits.Length * 32 - c; // Extract most significant bits @@ -1058,19 +1059,18 @@ public override int GetHashCode() if (_bits == null) return _sign; - int hash = _sign; - for (int iv = _bits.Length; --iv >= 0;) - hash = NumericsHelpers.CombineHash(hash, unchecked((int)_bits[iv])); - return hash; + + HashCode hash = default; + hash.Add(_sign); + hash.AddBytes(MemoryMarshal.AsBytes(_bits.AsSpan())); + return hash.ToHashCode(); } public override bool Equals([NotNullWhen(true)] object? obj) { AssertValid(); - if (!(obj is BigInteger)) - return false; - return Equals((BigInteger)obj); + return obj is BigInteger other && Equals(other); } public bool Equals(long other) @@ -1193,9 +1193,9 @@ public int CompareTo(object? obj) { if (obj == null) return 1; - if (!(obj is BigInteger)) + if (obj is not BigInteger bigInt) throw new ArgumentException(SR.Argument_MustBeBigInt, nameof(obj)); - return CompareTo((BigInteger)obj); + return CompareTo(bigInt); } /// @@ -1363,13 +1363,13 @@ private enum GetBytesMode { AllocateArray, Count, Span } // because a bits array of all zeros would represent 0, and this case // would be encoded as _bits = null and _sign = 0. Debug.Assert(bits.Length > 0); - Debug.Assert(bits[bits.Length - 1] != 0); + Debug.Assert(bits[^1] != 0); while (bits[nonZeroDwordIndex] == 0U) { nonZeroDwordIndex++; } - highDword = ~bits[bits.Length - 1]; + highDword = ~bits[^1]; if (bits.Length - 1 == nonZeroDwordIndex) { // This will not overflow because highDword is less than or equal to uint.MaxValue - 1. @@ -1381,7 +1381,7 @@ private enum GetBytesMode { AllocateArray, Count, Span } { Debug.Assert(sign == 1); highByte = 0x00; - highDword = bits[bits.Length - 1]; + highDword = bits[^1]; } byte msb; @@ -1522,12 +1522,13 @@ private int WriteTo(Span buffer) highDWord = 0; } - // Find highest significant byte - int msb; - for (msb = buffer.Length - 2; msb > 0; msb--) + // Find highest significant byte and ensure high bit is 0 if positive, 1 if negative + int msb = buffer.Length - 2; + while (msb > 0 && buffer[msb] == highDWord) { - if (buffer[msb] != highDWord) break; + msb--; } + // Ensure high bit is 0 if positive, 1 if negative bool needExtraByte = (buffer[msb] & 0x80000000) != (highDWord & 0x80000000); int count; @@ -1535,13 +1536,13 @@ private int WriteTo(Span buffer) if (needExtraByte) { count = msb + 2; - buffer = buffer.Slice(0, count); - buffer[buffer.Length - 1] = highDWord; + buffer = buffer[..count]; + buffer[^1] = highDWord; } else { count = msb + 1; - buffer = buffer.Slice(0, count); + buffer = buffer[..count]; } return count; @@ -1924,11 +1925,11 @@ public static explicit operator double(BigInteger value) return double.NegativeInfinity; } - ulong h = bits[length - 1]; - ulong m = length > 1 ? bits[length - 2] : 0; - ulong l = length > 2 ? bits[length - 3] : 0; + ulong h = bits[^1]; + ulong m = length > 1 ? bits[^2] : 0; + ulong l = length > 2 ? bits[^3] : 0; - int z = NumericsHelpers.CbitHighZero((uint)h); + int z = BitOperations.LeadingZeroCount((uint)h); int exp = (length - 2) * 32 - z; ulong man = (h << 32 + z) | (m << z) | (l >> 32 - z); @@ -2682,7 +2683,7 @@ public long GetBitLength() else { bitsArrayLength = bits.Length; - highValue = bits[bitsArrayLength - 1]; + highValue = bits[^1]; } long bitLength = bitsArrayLength * 32L - BitOperations.LeadingZeroCount(highValue); @@ -2757,7 +2758,7 @@ private void AssertValid() // Wasted space: _bits[0] could have been packed into _sign Debug.Assert(_bits.Length > 1 || _bits[0] >= kuMaskHighBit); // Wasted space: leading zeros could have been truncated - Debug.Assert(_bits[_bits.Length - 1] != 0); + Debug.Assert(_bits[^1] != 0); } else { diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs index 8ec356e6dc215..3c4a25b87f734 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Globalization; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -843,4 +842,4 @@ public static explicit operator Complex(decimal value) return new Complex((double)value, 0.0); } } -} +} \ No newline at end of file From 2a943001b09a201062f3b35253f9b03595ef49b1 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 6 Aug 2021 00:30:32 +0300 Subject: [PATCH 84/88] Review feedback (use const for stackalloc) --- .../src/System/Numerics/BigInteger.cs | 224 +++++++++--------- .../Numerics/BigIntegerCalculator.DivRem.cs | 6 +- .../Numerics/BigIntegerCalculator.GcdInv.cs | 6 +- .../Numerics/BigIntegerCalculator.PowMod.cs | 84 +++---- .../Numerics/BigIntegerCalculator.SquMul.cs | 30 +-- .../src/System/Numerics/BigNumber.cs | 10 +- 6 files changed, 180 insertions(+), 180 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 674f8690ff302..06b76a0fa0c8a 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -744,13 +744,13 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big uint[]? bitsFromPool = null; int size = dividend._bits.Length; - Span quotient = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); try { - //may throw DivideByZeroException + // may throw DivideByZeroException BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient, out rest); remainder = dividend._sign < 0 ? -1 * rest : rest; @@ -774,15 +774,15 @@ stackalloc uint[size] { uint[]? remainderFromPool = null; int size = dividend._bits.Length; - Span rest = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (remainderFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span rest = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : remainderFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); uint[]? quotientFromPool = null; size = dividend._bits.Length - divisor._bits.Length + 1; - Span quotient = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : quotientFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Divide(dividend._bits, divisor._bits, quotient, rest); @@ -897,9 +897,9 @@ private static BigInteger GreatestCommonDivisor(ReadOnlySpan leftBits, Rea } else if (rightBits.Length == 2) { - Span bits = leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[leftBits.Length] - : (bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).AsSpan(0, leftBits.Length); + Span bits = (leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).Slice(0, leftBits.Length); BigIntegerCalculator.Remainder(leftBits, rightBits, bits); @@ -910,9 +910,9 @@ stackalloc uint[leftBits.Length] } else { - Span bits = leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[leftBits.Length] - : (bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).AsSpan(0, leftBits.Length); + Span bits = (leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).Slice(0, leftBits.Length); BigIntegerCalculator.Gcd(leftBits, rightBits, bits); result = new BigInteger(bits, false); @@ -966,9 +966,9 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege { int size = (modulus._bits?.Length ?? 1) << 1; uint[]? bitsFromPool = null; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); bits.Clear(); if (trivialValue) { @@ -1027,9 +1027,9 @@ public static BigInteger Pow(BigInteger value, int exponent) return value; int size = BigIntegerCalculator.PowBound(power, 1); - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); bits.Clear(); BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), power, bits); @@ -1038,9 +1038,9 @@ stackalloc uint[size] else { int size = BigIntegerCalculator.PowBound(power, value._bits!.Length); - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); bits.Clear(); BigIntegerCalculator.Pow(value._bits, power, bits); @@ -1591,9 +1591,9 @@ private static BigInteger Add(ReadOnlySpan leftBits, int leftSign, ReadOnl Debug.Assert(!rightBits.IsEmpty); int size = rightBits.Length + 1; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Add(rightBits, NumericsHelpers.Abs(leftSign), bits); result = new BigInteger(bits, leftSign < 0); @@ -1603,9 +1603,9 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty); int size = leftBits.Length + 1; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Add(leftBits, NumericsHelpers.Abs(rightSign), bits); result = new BigInteger(bits, leftSign < 0); @@ -1615,9 +1615,9 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); int size = rightBits.Length + 1; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Add(rightBits, leftBits, bits); result = new BigInteger(bits, leftSign < 0); @@ -1627,9 +1627,9 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); int size = leftBits.Length + 1; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Add(leftBits, rightBits, bits); result = new BigInteger(bits, leftSign < 0); @@ -1669,9 +1669,9 @@ private static BigInteger Subtract(ReadOnlySpan leftBits, int leftSign, Re Debug.Assert(!rightBits.IsEmpty); int size = rightBits.Length; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Subtract(rightBits, NumericsHelpers.Abs(leftSign), bits); result = new BigInteger(bits, leftSign >= 0); @@ -1681,9 +1681,9 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty); int size = leftBits.Length; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Subtract(leftBits, NumericsHelpers.Abs(rightSign), bits); result = new BigInteger(bits, leftSign < 0); @@ -1691,9 +1691,9 @@ stackalloc uint[size] else if (BigIntegerCalculator.Compare(leftBits, rightBits) < 0) { int size = rightBits.Length; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Subtract(rightBits, leftBits, bits); result = new BigInteger(bits, leftSign >= 0); @@ -1703,9 +1703,9 @@ stackalloc uint[size] Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty); int size = leftBits.Length; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Subtract(leftBits, rightBits, bits); result = new BigInteger(bits, leftSign < 0); @@ -1975,23 +1975,23 @@ public static explicit operator decimal(BigInteger value) uint[]? leftBufferFromPool = null; int size = (left._bits?.Length ?? 1) + 1; - Span x = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : leftBufferFromPool = ArrayPool.Shared.Rent(size); + Span x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : leftBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; - Span y = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : rightBufferFromPool = ArrayPool.Shared.Rent(size); + Span y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : rightBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); - Span z = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span z = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); for (int i = 0; i < z.Length; i++) { @@ -2031,23 +2031,23 @@ stackalloc uint[size] uint[]? leftBufferFromPool = null; int size = (left._bits?.Length ?? 1) + 1; - Span x = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : leftBufferFromPool = ArrayPool.Shared.Rent(size); + Span x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : leftBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; - Span y = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : rightBufferFromPool = ArrayPool.Shared.Rent(size); + Span y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : rightBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); - Span z = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span z = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); for (int i = 0; i < z.Length; i++) { @@ -2082,23 +2082,23 @@ stackalloc uint[size] uint[]? leftBufferFromPool = null; int size = (left._bits?.Length ?? 1) + 1; - Span x = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : leftBufferFromPool = ArrayPool.Shared.Rent(size); + Span x = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : leftBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); x = x.Slice(0, left.WriteTo(x)); uint[]? rightBufferFromPool = null; size = (right._bits?.Length ?? 1) + 1; - Span y = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : rightBufferFromPool = ArrayPool.Shared.Rent(size); + Span y = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : rightBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); y = y.Slice(0, right.WriteTo(y)); uint[]? resultBufferFromPool = null; size = Math.Max(x.Length, y.Length); - Span z = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (resultBufferFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span z = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); for (int i = 0; i < z.Length; i++) { @@ -2132,16 +2132,16 @@ stackalloc uint[size] uint[]? xdFromPool = null; int xl = value._bits?.Length ?? 1; - Span xd = xl <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[xl] - : (xdFromPool = ArrayPool.Shared.Rent(xl)).AsSpan(0, xl); + Span xd = (xl <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : xdFromPool = ArrayPool.Shared.Rent(xl)).Slice(0, xl); bool negx = value.GetPartsForBitManipulation(xd); int zl = xl + digitShift + 1; uint[]? zdFromPool = null; - Span zd = zl <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[zl] - : (zdFromPool = ArrayPool.Shared.Rent(zl)).AsSpan(0, zl); + Span zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : zdFromPool = ArrayPool.Shared.Rent(zl)).Slice(0, zl); zd.Clear(); if (smallShift == 0) @@ -2188,9 +2188,9 @@ stackalloc uint[zl] uint[]? xdFromPool = null; int xl = value._bits?.Length ?? 1; - Span xd = xl <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[xl] - : (xdFromPool = ArrayPool.Shared.Rent(xl)).AsSpan(0, xl); + Span xd = (xl <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : xdFromPool = ArrayPool.Shared.Rent(xl)).Slice(0, xl); bool negx = value.GetPartsForBitManipulation(xd); bool trackSignBit = false; @@ -2212,9 +2212,9 @@ stackalloc uint[xl] uint[]? zdFromPool = null; int zl = Math.Max(xl - digitShift, 0) + (trackSignBit ? 1 : 0); - Span zd = zl <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[zl] - : (zdFromPool = ArrayPool.Shared.Rent(zl)).AsSpan(0, zl); + Span zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : zdFromPool = ArrayPool.Shared.Rent(zl)).Slice(0, zl); zd.Clear(); if (smallShift == 0) @@ -2322,9 +2322,9 @@ private static BigInteger Multiply(ReadOnlySpan left, int leftSign, ReadOn Debug.Assert(!right.IsEmpty); int size = right.Length + 1; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Multiply(right, NumericsHelpers.Abs(leftSign), bits); result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); @@ -2334,9 +2334,9 @@ stackalloc uint[size] Debug.Assert(!left.IsEmpty); int size = left.Length + 1; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Multiply(left, NumericsHelpers.Abs(rightSign), bits); result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); @@ -2344,9 +2344,9 @@ stackalloc uint[size] else if (left.Length == right.Length && Unsafe.AreSame(ref Unsafe.AsRef(in left[0]), ref Unsafe.AsRef(in right[0]))) { int size = left.Length + right.Length; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Square(left, bits); result = new BigInteger(bits, false); @@ -2356,9 +2356,9 @@ stackalloc uint[size] Debug.Assert(!left.IsEmpty && !right.IsEmpty); int size = left.Length + right.Length; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); bits.Clear(); BigIntegerCalculator.Multiply(right, left, bits); @@ -2369,9 +2369,9 @@ stackalloc uint[size] Debug.Assert(!left.IsEmpty && !right.IsEmpty); int size = left.Length + right.Length; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); bits.Clear(); BigIntegerCalculator.Multiply(left, right, bits); @@ -2411,9 +2411,9 @@ stackalloc uint[size] Debug.Assert(dividend._bits != null); int size = dividend._bits.Length; - Span quotient = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : quotientFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); try { @@ -2437,9 +2437,9 @@ stackalloc uint[size] else { int size = dividend._bits.Length - divisor._bits.Length + 1; - Span quotient = size < BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (quotientFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span quotient = ((uint)size < BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : quotientFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Divide(dividend._bits, divisor._bits, quotient); var result = new BigInteger(quotient, (dividend._sign < 0) ^ (divisor._sign < 0)); @@ -2487,9 +2487,9 @@ stackalloc uint[size] uint[]? bitsFromPool = null; int size = dividend._bits.Length; - Span bits = size <= BigIntegerCalculator.StackAllocThreshold ? - stackalloc uint[size] - : (bitsFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ? + stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Remainder(dividend._bits, divisor._bits, bits); var result = new BigInteger(bits, dividend._sign < 0); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 6eac0b406108d..be733d5816a29 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -88,9 +88,9 @@ public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Spa // NOTE: left will get overwritten, we need a local copy // However, mutated left is not used afterwards, so use array pooling or stack alloc - Span leftCopy = left.Length <= StackAllocThreshold ? - stackalloc uint[left.Length] - : (leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).AsSpan(0, left.Length); + Span leftCopy = (left.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).Slice(0, left.Length); left.CopyTo(leftCopy); Divide(leftCopy, right, quotient); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 232cb916a930b..c9944c1ba95bb 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -64,9 +64,9 @@ public static void Gcd(ReadOnlySpan left, ReadOnlySpan right, Span rightCopy = right.Length <= StackAllocThreshold ? - stackalloc uint[right.Length] - : (rightCopyFromPool = ArrayPool.Shared.Rent(right.Length)).AsSpan(0, right.Length); + Span rightCopy = (right.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : rightCopyFromPool = ArrayPool.Shared.Rent(right.Length)).Slice(0, right.Length); right.CopyTo(rightCopy); Gcd(result, rightCopy); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index 207c551ec5ff0..f14fe98df0e68 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -24,15 +24,15 @@ public static void Pow(ReadOnlySpan value, uint power, Span bits) Debug.Assert(bits.Length == PowBound(power, value.Length)); uint[]? tempFromPool = null; - Span temp = bits.Length <= StackAllocThreshold ? - stackalloc uint[bits.Length] - : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); + Span temp = (bits.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length); temp.Clear(); uint[]? valueCopyFromPool = null; - Span valueCopy = bits.Length <= StackAllocThreshold ? - stackalloc uint[bits.Length] - : (valueCopyFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); + Span valueCopy = (bits.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : valueCopyFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length); value.CopyTo(valueCopy); valueCopy.Slice(value.Length).Clear(); @@ -215,9 +215,9 @@ public static void Pow(ReadOnlySpan value, uint power, uint[]? valueCopyFromPool = null; int size = Math.Max(value.Length, bits.Length); - Span valueCopy = size <= StackAllocThreshold ? - stackalloc uint[size] - : (valueCopyFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span valueCopy = (size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : valueCopyFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); valueCopy.Clear(); if (value.Length > modulus.Length) @@ -230,9 +230,9 @@ stackalloc uint[size] } uint[]? tempFromPool = null; - Span temp = bits.Length <= StackAllocThreshold ? - stackalloc uint[bits.Length] - : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); + Span temp = (bits.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length); temp.Clear(); PowCore(valueCopy, ActualLength(valueCopy), power, modulus, temp, bits); @@ -260,9 +260,9 @@ public static void Pow(ReadOnlySpan value, ReadOnlySpan power, int size = Math.Max(value.Length, bits.Length); uint[]? valueCopyFromPool = null; - Span valueCopy = size <= StackAllocThreshold ? - stackalloc uint[size] - : (valueCopyFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span valueCopy = (size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : valueCopyFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); valueCopy.Clear(); if (value.Length > modulus.Length) @@ -275,9 +275,9 @@ stackalloc uint[size] } uint[]? tempFromPool = null; - Span temp = bits.Length <= StackAllocThreshold ? - stackalloc uint[bits.Length] - : (tempFromPool = ArrayPool.Shared.Rent(bits.Length)).AsSpan(0, bits.Length); + Span temp = (bits.Length <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length); temp.Clear(); PowCore(valueCopy, ActualLength(valueCopy), power, modulus, temp, bits); @@ -312,29 +312,29 @@ private static void PowCore(Span value, int valueLength, { int size = modulus.Length * 2 + 1; uint[]? rFromPool = null; - Span r = size <= StackAllocThreshold ? - stackalloc uint[size] - : (rFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span r = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : rFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); r.Clear(); size = r.Length - modulus.Length + 1; uint[]? muFromPool = null; - Span mu = size <= StackAllocThreshold ? - stackalloc uint[size] - : (muFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span mu = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : muFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); mu.Clear(); size = modulus.Length * 2 + 2; uint[]? q1FromPool = null; - Span q1 = size <= StackAllocThreshold ? - stackalloc uint[size] - : (q1FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span q1 = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : q1FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); q1.Clear(); uint[]? q2FromPool = null; - Span q2 = size <= StackAllocThreshold ? - stackalloc uint[size] - : (q2FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span q2 = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : q2FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); q2.Clear(); FastReducer reducer = new FastReducer(modulus, r, mu, q1, q2); @@ -368,29 +368,29 @@ private static void PowCore(Span value, int valueLength, { int size = modulus.Length * 2 + 1; uint[]? rFromPool = null; - Span r = size <= StackAllocThreshold ? - stackalloc uint[size] - : (rFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span r = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : rFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); r.Clear(); size = r.Length - modulus.Length + 1; uint[]? muFromPool = null; - Span mu = size <= StackAllocThreshold ? - stackalloc uint[size] - : (muFromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span mu = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : muFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); mu.Clear(); size = modulus.Length * 2 + 2; uint[]? q1FromPool = null; - Span q1 = size <= StackAllocThreshold ? - stackalloc uint[size] - : (q1FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span q1 = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : q1FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); q1.Clear(); uint[]? q2FromPool = null; - Span q2 = size <= StackAllocThreshold ? - stackalloc uint[size] - : (q2FromPool = ArrayPool.Shared.Rent(size)).AsSpan(0, size); + Span q2 = ((uint)size <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : q2FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); q2.Clear(); FastReducer reducer = new FastReducer(modulus, r, mu, q1, q2); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index e0ca57ac1054a..885e2bd19f078 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -98,16 +98,16 @@ public static void Square(ReadOnlySpan value, Span bits) int foldLength = valueHigh.Length + 1; uint[]? foldFromPool = null; - Span fold = foldLength <= StackAllocThreshold ? - stackalloc uint[foldLength] - : (foldFromPool = ArrayPool.Shared.Rent(foldLength)).AsSpan(0, foldLength); + Span fold = ((uint)foldLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : foldFromPool = ArrayPool.Shared.Rent(foldLength)).Slice(0, foldLength); fold.Clear(); int coreLength = foldLength + foldLength; uint[]? coreFromPool = null; - Span core = coreLength <= StackAllocThreshold ? - stackalloc uint[coreLength] - : (coreFromPool = ArrayPool.Shared.Rent(coreLength)).AsSpan(0, coreLength); + Span core = ((uint)coreLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : coreFromPool = ArrayPool.Shared.Rent(coreLength)).Slice(0, coreLength); core.Clear(); // ... compute z_a = a_1 + a_0 (call it fold...) @@ -237,23 +237,23 @@ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, S int leftFoldLength = leftHigh.Length + 1; uint[]? leftFoldFromPool = null; - Span leftFold = leftFoldLength <= StackAllocThreshold ? - stackalloc uint[leftFoldLength] - : (leftFoldFromPool = ArrayPool.Shared.Rent(leftFoldLength)).AsSpan(0, leftFoldLength); + Span leftFold = ((uint)leftFoldLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : leftFoldFromPool = ArrayPool.Shared.Rent(leftFoldLength)).Slice(0, leftFoldLength); leftFold.Clear(); int rightFoldLength = rightHigh.Length + 1; uint[]? rightFoldFromPool = null; - Span rightFold = rightFoldLength <= StackAllocThreshold ? - stackalloc uint[rightFoldLength] - : (rightFoldFromPool = ArrayPool.Shared.Rent(rightFoldLength)).AsSpan(0, rightFoldLength); + Span rightFold = ((uint)rightFoldLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : rightFoldFromPool = ArrayPool.Shared.Rent(rightFoldLength)).Slice(0, rightFoldLength); rightFold.Clear(); int coreLength = leftFoldLength + rightFoldLength; uint[]? coreFromPool = null; - Span core = coreLength <= StackAllocThreshold ? - stackalloc uint[coreLength] - : (coreFromPool = ArrayPool.Shared.Rent(coreLength)).AsSpan(0, coreLength); + Span core = ((uint)coreLength <= StackAllocThreshold ? + stackalloc uint[StackAllocThreshold] + : coreFromPool = ArrayPool.Shared.Rent(coreLength)).Slice(0, coreLength); core.Clear(); // ... compute z_a = a_1 + a_0 (call it fold...) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index c6901321b165f..2d7395e2e83dd 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -410,11 +410,11 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt bool isNegative = HexConverter.FromChar(number.digits[0]) >= 8; uint partialValue = (isNegative && partialDigitCount > 0) ? 0xFFFFFFFFu : 0; - int[]? arrayFromPool = null; + uint[]? arrayFromPool = null; - Span bitsBuffer = (blockCount <= BigIntegerCalculator.StackAllocThreshold) - ? stackalloc uint[blockCount] - : MemoryMarshal.Cast((arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount)); + Span bitsBuffer = ((uint)blockCount <= BigIntegerCalculator.StackAllocThreshold + ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold] + : arrayFromPool = ArrayPool.Shared.Rent(blockCount)).Slice(0, blockCount); int bitsBufferPos = blockCount - 1; @@ -489,7 +489,7 @@ private static bool HexNumberToBigInteger(ref BigNumberBuffer number, out BigInt { if (arrayFromPool != null) { - ArrayPool.Shared.Return(arrayFromPool); + ArrayPool.Shared.Return(arrayFromPool); } } } From 497122433407ec2e5f7141425d291b7e6138ebc9 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 24 Aug 2021 23:33:15 +0300 Subject: [PATCH 85/88] Review feedback --- .../src/System/Numerics/BigInteger.cs | 75 +++++++++++-------- .../Numerics/BigIntegerCalculator.DivRem.cs | 29 +++---- .../BigIntegerCalculator.FastReducer.cs | 4 +- .../Numerics/BigIntegerCalculator.PowMod.cs | 7 +- .../Numerics/BigIntegerCalculator.SquMul.cs | 2 +- .../src/System/Numerics/NumericsHelpers.cs | 50 +------------ 6 files changed, 59 insertions(+), 108 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index da509bddcf52b..c3e550eec5d8f 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -5,8 +5,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System.Numerics { @@ -859,7 +857,7 @@ public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right Debug.Assert(right._bits != null); return left._sign != 0 ? BigIntegerCalculator.Gcd(right._bits, NumericsHelpers.Abs(left._sign)) - : new BigInteger(right._bits, false); + : new BigInteger(right._bits, negative: false); } if (trivialRight) @@ -867,7 +865,7 @@ public static BigInteger GreatestCommonDivisor(BigInteger left, BigInteger right Debug.Assert(left._bits != null); return right._sign != 0 ? BigIntegerCalculator.Gcd(left._bits, NumericsHelpers.Abs(right._sign)) - : new BigInteger(left._bits, false); + : new BigInteger(left._bits, negative: false); } Debug.Assert(left._bits != null && right._bits != null); @@ -915,7 +913,7 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] : bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).Slice(0, leftBits.Length); BigIntegerCalculator.Gcd(leftBits, rightBits, bits); - result = new BigInteger(bits, false); + result = new BigInteger(bits, negative: false); } if (bitsFromPool != null) @@ -1057,13 +1055,15 @@ public override int GetHashCode() { AssertValid(); - if (_bits == null) + if (_bits is null) return _sign; - HashCode hash = default; - hash.Add(_sign); - hash.AddBytes(MemoryMarshal.AsBytes(_bits.AsSpan())); - return hash.ToHashCode(); + int hash = _sign; + for (int iv = _bits.Length; --iv >= 0;) + hash = unchecked((int)CombineHash((uint)hash, _bits[iv])); + return hash; + + static uint CombineHash(uint u1, uint u2) => ((u1 << 7) | (u1 >> 25)) ^ u2; } public override bool Equals([NotNullWhen(true)] object? obj) @@ -1515,7 +1515,7 @@ private int WriteTo(Span buffer) buffer = buffer.Slice(0, _bits.Length + 1); if (_sign == -1) { - NumericsHelpers.DangerousMakeTwosComplement(buffer[..^1]); // Mutates dwords + NumericsHelpers.DangerousMakeTwosComplement(buffer.Slice(0, buffer.Length - 1)); // Mutates dwords highDWord = uint.MaxValue; } else @@ -2123,12 +2123,16 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] public static BigInteger operator <<(BigInteger value, int shift) { - if (shift == 0) return value; - else if (shift == int.MinValue) return ((value >> int.MaxValue) >> 1); - else if (shift < 0) return value >> -shift; + if (shift == 0) + return value; + + if (shift == int.MinValue) + return ((value >> int.MaxValue) >> 1); + + if (shift < 0) + return value >> -shift; - int digitShift = shift / kcbitUint; - int smallShift = shift - (digitShift * kcbitUint); + (int digitShift, int smallShift) = Math.DivRem(shift, kcbitUint); uint[]? xdFromPool = null; int xl = value._bits?.Length ?? 1; @@ -2144,9 +2148,10 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] : zdFromPool = ArrayPool.Shared.Rent(zl)).Slice(0, zl); zd.Clear(); + uint carry = 0; if (smallShift == 0) { - for (int i = 0; i < xl; i++) + for (int i = 0; i < xd.Length; i++) { zd[i + digitShift] = xd[i]; } @@ -2154,17 +2159,17 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] else { int carryShift = kcbitUint - smallShift; - uint carry = 0; int i; - for (i = 0; i < xl; i++) + for (i = 0; i < xd.Length; i++) { uint rot = xd[i]; zd[i + digitShift] = rot << smallShift | carry; carry = rot >> carryShift; } - zd[i + digitShift] = carry; } + zd[zd.Length - 1] = carry; + var result = new BigInteger(zd, negx); if (xdFromPool != null) @@ -2177,12 +2182,16 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] public static BigInteger operator >>(BigInteger value, int shift) { - if (shift == 0) return value; - else if (shift == int.MinValue) return ((value << int.MaxValue) << 1); - else if (shift < 0) return value << -shift; + if (shift == 0) + return value; + + if (shift == int.MinValue) + return ((value << int.MaxValue) << 1); + + if (shift < 0) + return value << -shift; - int digitShift = shift / kcbitUint; - int smallShift = shift - (digitShift * kcbitUint); + (int digitShift, int smallShift) = Math.DivRem(shift, kcbitUint); BigInteger result; @@ -2197,14 +2206,14 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] if (negx) { - if (shift >= (kcbitUint * xl)) + if (shift >= (kcbitUint * xd.Length)) { result = MinusOne; goto exit; } NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd - if (xd[^1] == 0) + if (xd[xd.Length - 1] == 0) { trackSignBit = true; } @@ -2219,7 +2228,7 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] if (smallShift == 0) { - for (int i = xl - 1; i >= digitShift; i--) + for (int i = xd.Length - 1; i >= digitShift; i--) { zd[i - digitShift] = xd[i]; } @@ -2228,10 +2237,10 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] { int carryShift = kcbitUint - smallShift; uint carry = 0; - for (int i = xl - 1; i >= digitShift; i--) + for (int i = xd.Length - 1; i >= digitShift; i--) { uint rot = xd[i]; - if (negx && i == xl - 1) + if (negx && i == xd.Length - 1) // Sign-extend the first shift for negative ints then let the carry propagate zd[i - digitShift] = (rot >> smallShift) | (0xFFFFFFFF << carryShift); else @@ -2245,7 +2254,7 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] NumericsHelpers.DangerousMakeTwosComplement(zd); // Mutates zd if (trackSignBit) - zd[^1] = 1; + zd[zd.Length - 1] = 1; } result = new BigInteger(zd, negx); @@ -2341,7 +2350,7 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] BigIntegerCalculator.Multiply(left, NumericsHelpers.Abs(rightSign), bits); result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0)); } - else if (left.Length == right.Length && Unsafe.AreSame(ref Unsafe.AsRef(in left[0]), ref Unsafe.AsRef(in right[0]))) + else if (left == right) { int size = left.Length + right.Length; Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ? @@ -2349,7 +2358,7 @@ stackalloc uint[BigIntegerCalculator.StackAllocThreshold] : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size); BigIntegerCalculator.Square(left, bits); - result = new BigInteger(bits, false); + result = new BigInteger(bits, negative: false); } else if (left.Length < right.Length) { diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index d56d833b191b3..33da6e7317878 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -3,8 +3,6 @@ using System.Buffers; using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System.Numerics { @@ -120,19 +118,13 @@ private static void Divide(Span left, ReadOnlySpan right, Span Debug.Assert(bits.Length == left.Length - right.Length + 1 || bits.Length == 0); - // Switching to managed references helps eliminating - // index bounds check... - ref uint leftPtr = ref MemoryMarshal.GetReference(left); - ref uint rightPtr = ref MemoryMarshal.GetReference(right); - ref uint resultPtr = ref MemoryMarshal.GetReference(bits); - // Executes the "grammar-school" algorithm for computing q = a / b. // Before calculating q_i, we get more bits into the highest bit // block of the divisor. Thus, guessing digits of the quotient // will be more precise. Additionally we'll get r = a % b. - uint divHi = Unsafe.Add(ref rightPtr, right.Length - 1); - uint divLo = right.Length > 1 ? Unsafe.Add(ref rightPtr, right.Length - 2) : 0; + uint divHi = right[right.Length - 1]; + uint divLo = right.Length > 1 ? right[right.Length - 2] : 0; // We measure the leading zeros of the divisor int shift = LeadingZeros(divHi); @@ -141,7 +133,7 @@ private static void Divide(Span left, ReadOnlySpan right, Span // And, we make sure the most significant bit is set if (shift > 0) { - uint divNx = right.Length > 2 ? Unsafe.Add(ref rightPtr, right.Length - 3) : 0; + uint divNx = right.Length > 2 ? right[right.Length - 3] : 0; divHi = (divHi << shift) | (divLo >> backShift); divLo = (divLo << shift) | (divNx >> backShift); @@ -152,15 +144,15 @@ private static void Divide(Span left, ReadOnlySpan right, Span for (int i = left.Length; i >= right.Length; i--) { int n = i - right.Length; - uint t = (uint)i < (uint)left.Length ? Unsafe.Add(ref leftPtr, i) : 0; + uint t = (uint)i < (uint)left.Length ? left[i] : 0; - ulong valHi = ((ulong)t << 32) | Unsafe.Add(ref leftPtr, i - 1); - uint valLo = i > 1 ? Unsafe.Add(ref leftPtr, i - 2) : 0; + ulong valHi = ((ulong)t << 32) | left[i - 1]; + uint valLo = i > 1 ? left[i - 2] : 0; // We shifted the divisor, we shift the dividend too if (shift > 0) { - uint valNx = i > 2 ? Unsafe.Add(ref leftPtr, i - 3) : 0; + uint valNx = i > 2 ? left[i - 3] : 0; valHi = (valHi << shift) | (valLo >> backShift); valLo = (valLo << shift) | (valNx >> backShift); @@ -193,10 +185,11 @@ private static void Divide(Span left, ReadOnlySpan right, Span } // We have the digit! - if (!bits.IsEmpty) - Unsafe.Add(ref resultPtr, n) = (uint)digit; + if ((uint)n < (uint)bits.Length) + bits[n] = (uint)digit; + if ((uint)i < (uint)left.Length) - Unsafe.Add(ref leftPtr, i) = 0; + left[i] = 0; } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index b565aaff5b07c..0b56d2b52e245 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -29,7 +29,7 @@ public FastReducer(ReadOnlySpan modulus, Span r, Span mu, Span Debug.Assert(q2.Length == modulus.Length * 2 + 2); // Let r = 4^k, with 2^k > m - r[^1] = 1; + r[r.Length - 1] = 1; // Let mu = 4^k / m Divide(r, modulus, mu); @@ -76,8 +76,6 @@ private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Spa // preceding division by 2^(32*k). To spare memory allocations // we write the result to an already allocated memory. - bits.Clear(); - if (left.Length > k) { left = left.Slice(k); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs index f14fe98df0e68..0407feff54fe3 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -3,7 +3,6 @@ using System.Buffers; using System.Diagnostics; -using System.Runtime.InteropServices; namespace System.Numerics { @@ -16,7 +15,7 @@ internal static partial class BigIntegerCalculator public static void Pow(uint value, uint power, Span bits) { - Pow(value != 0U ? MemoryMarshal.CreateReadOnlySpan(ref value, 1) : default, power, bits); + Pow(value != 0U ? stackalloc uint[1] { value } : default, power, bits); } public static void Pow(ReadOnlySpan value, uint power, Span bits) @@ -201,7 +200,7 @@ private static uint PowCore(ulong value, uint power, uint modulus, ulong result) public static void Pow(uint value, uint power, ReadOnlySpan modulus, Span bits) { - Pow(value != 0U ? MemoryMarshal.CreateReadOnlySpan(ref value, 1) : default, power, modulus, bits); + Pow(value != 0U ? stackalloc uint[1] { value } : default, power, modulus, bits); } public static void Pow(ReadOnlySpan value, uint power, @@ -246,7 +245,7 @@ stackalloc uint[StackAllocThreshold] public static void Pow(uint value, ReadOnlySpan power, ReadOnlySpan modulus, Span bits) { - Pow(value != 0U ? MemoryMarshal.CreateReadOnlySpan(ref value, 1) : default, power, modulus, bits); + Pow(value != 0U ? stackalloc uint[1] { value } : default, power, modulus, bits); } public static void Pow(ReadOnlySpan value, ReadOnlySpan power, diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 885e2bd19f078..2ca8f5bcee4d6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -165,7 +165,7 @@ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, S // Executes different algorithms for computing z = a * b // based on the actual length of b. If b is "small" enough - // we stick to the classic "grammar-school" method; f the + // we stick to the classic "grammar-school" method; for the // rest we switch to implementations with less complexity // albeit more overhead (which needs to pay off!). diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs index aba55a6e44408..ff9d263edcb51 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs @@ -59,7 +59,7 @@ public static double GetDoubleFromParts(int sign, int exp, ulong man) else { // Normalize so that 0x0010 0000 0000 0000 is the highest bit set. - int cbitShift = CbitHighZero(man) - 11; + int cbitShift = BitOperations.LeadingZeroCount(man) - 11; if (cbitShift < 0) man >>= -cbitShift; else @@ -140,53 +140,5 @@ public static uint Abs(int a) return ((uint)a ^ mask) - mask; } } - - public static uint CombineHash(uint u1, uint u2) - { - return ((u1 << 7) | (u1 >> 25)) ^ u2; - } - - public static int CombineHash(int n1, int n2) - { - return unchecked((int)CombineHash((uint)n1, (uint)n2)); - } - - public static int CbitHighZero(uint u) - { - if (u == 0) - return 32; - - int cbit = 0; - if ((u & 0xFFFF0000) == 0) - { - cbit += 16; - u <<= 16; - } - if ((u & 0xFF000000) == 0) - { - cbit += 8; - u <<= 8; - } - if ((u & 0xF0000000) == 0) - { - cbit += 4; - u <<= 4; - } - if ((u & 0xC0000000) == 0) - { - cbit += 2; - u <<= 2; - } - if ((u & 0x80000000) == 0) - cbit += 1; - return cbit; - } - - public static int CbitHighZero(ulong uu) - { - if ((uu & 0xFFFFFFFF00000000) == 0) - return 32 + CbitHighZero((uint)uu); - return CbitHighZero((uint)(uu >> 32)); - } } } From 020e7495c867d28ebebda0867de1cdaff2233ed0 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 24 Aug 2021 23:49:34 +0300 Subject: [PATCH 86/88] Unsafe code replaced with span indexer where possible --- .../Numerics/BigIntegerCalculator.AddSub.cs | 11 +++++----- .../Numerics/BigIntegerCalculator.SquMul.cs | 20 +++++++++---------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index 21390ebc1e5eb..066924ebba790 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -87,8 +87,8 @@ private static void AddSelf(Span left, ReadOnlySpan right) } for ( ; carry != 0 && i < left.Length; i++) { - long digit = Unsafe.Add(ref leftPtr, i) + carry; - Unsafe.Add(ref leftPtr, i) = (uint)digit; + long digit = left[i] + carry; + left[i] = (uint)digit; carry = digit >> 32; } @@ -161,7 +161,6 @@ private static void SubtractSelf(Span left, ReadOnlySpan right) // Switching to managed references helps eliminating // index bounds check... ref uint leftPtr = ref MemoryMarshal.GetReference(left); - ref uint rightPtr = ref MemoryMarshal.GetReference(right); // Executes the "grammar-school" algorithm for computing z = a - b. // Same as above, but we're writing the result directly to a and @@ -169,14 +168,14 @@ private static void SubtractSelf(Span left, ReadOnlySpan right) for (; i < right.Length; i++) { - long digit = (Unsafe.Add(ref leftPtr, i) + carry) - Unsafe.Add(ref rightPtr, i); + long digit = (Unsafe.Add(ref leftPtr, i) + carry) - right[i]; Unsafe.Add(ref leftPtr, i) = unchecked((uint)digit); carry = digit >> 32; } for (; carry != 0 && i < left.Length; i++) { - long digit = Unsafe.Add(ref leftPtr, i) + carry; - Unsafe.Add(ref leftPtr, i) = (uint)digit; + long digit = left[i] + carry; + left[i] = (uint)digit; carry = digit >> 32; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 2ca8f5bcee4d6..4aeae650e76e6 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -35,7 +35,6 @@ public static void Square(ReadOnlySpan value, Span bits) { // Switching to managed references helps eliminating // index bounds check... - ref uint valuePtr = ref MemoryMarshal.GetReference(value); ref uint resultPtr = ref MemoryMarshal.GetReference(bits); // Squares the bits using the "grammar-school" method. @@ -52,11 +51,11 @@ public static void Square(ReadOnlySpan value, Span bits) for (int i = 0; i < value.Length; i++) { ulong carry = 0UL; - uint v = Unsafe.Add(ref valuePtr, i); + uint v = value[i]; for (int j = 0; j < i; j++) { ulong digit1 = Unsafe.Add(ref resultPtr, i + j) + carry; - ulong digit2 = (ulong)Unsafe.Add(ref valuePtr, j) * v; + ulong digit2 = (ulong)value[j] * v; Unsafe.Add(ref resultPtr, i + j) = unchecked((uint)(digit1 + (digit2 << 1))); carry = (digit2 + (digit1 >> 1)) >> 31; } @@ -176,8 +175,6 @@ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, S { // Switching to managed references helps eliminating // index bounds check... - ref uint leftPtr = ref MemoryMarshal.GetReference(left); - ref uint rightPtr = ref MemoryMarshal.GetReference(right); ref uint resultPtr = ref MemoryMarshal.GetReference(bits); // Multiplies the bits using the "grammar-school" method. @@ -193,7 +190,7 @@ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, S for (int j = 0; j < left.Length; j++) { ref uint elementPtr = ref Unsafe.Add(ref resultPtr, i + j); - ulong digits = elementPtr + carry + (ulong)Unsafe.Add(ref leftPtr, j) * Unsafe.Add(ref rightPtr, i); + ulong digits = elementPtr + carry + (ulong)left[j] * right[i]; elementPtr = unchecked((uint)digits); carry = digits >> 32; } @@ -299,25 +296,26 @@ private static void SubtractCore(ReadOnlySpan left, ReadOnlySpan rig // Switching to managed references helps eliminating // index bounds check... ref uint leftPtr = ref MemoryMarshal.GetReference(left); - ref uint rightPtr = ref MemoryMarshal.GetReference(right); ref uint corePtr = ref MemoryMarshal.GetReference(core); for ( ; i < right.Length; i++) { - long digit = (Unsafe.Add(ref corePtr, i) + carry) - Unsafe.Add(ref leftPtr, i) - Unsafe.Add(ref rightPtr, i); + long digit = (Unsafe.Add(ref corePtr, i) + carry) - Unsafe.Add(ref leftPtr, i) - right[i]; Unsafe.Add(ref corePtr, i) = unchecked((uint)digit); carry = digit >> 32; } + for ( ; i < left.Length; i++) { - long digit = (Unsafe.Add(ref corePtr, i) + carry) - Unsafe.Add(ref leftPtr, i); + long digit = (Unsafe.Add(ref corePtr, i) + carry) - left[i]; Unsafe.Add(ref corePtr, i) = unchecked((uint)digit); carry = digit >> 32; } + for ( ; carry != 0 && i < core.Length; i++) { - long digit = Unsafe.Add(ref corePtr, i) + carry; - Unsafe.Add(ref corePtr, i) = (uint)digit; + long digit = core[i] + carry; + core[i] = (uint)digit; carry = digit >> 32; } } From 68306d12665000719de1abd63f24fdd5d6245ae5 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 26 Aug 2021 11:54:13 +0300 Subject: [PATCH 87/88] Removed unused local --- .../src/System/Numerics/BigIntegerCalculator.AddSub.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index 066924ebba790..c2ac712ec9e67 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -73,7 +73,6 @@ private static void AddSelf(Span left, ReadOnlySpan right) // Switching to managed references helps eliminating // index bounds check... ref uint leftPtr = ref MemoryMarshal.GetReference(left); - ref uint rightPtr = ref MemoryMarshal.GetReference(right); // Executes the "grammar-school" algorithm for computing z = a + b. // Same as above, but we're writing the result directly to a and From 602574f0fbb6449eabbc5108203c4ce8f9fd7be1 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 8 Oct 2021 10:08:21 +0300 Subject: [PATCH 88/88] Review feedback --- .../Numerics/BigIntegerCalculator.DivRem.cs | 36 +------------------ .../Numerics/BigIntegerCalculator.GcdInv.cs | 2 +- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index 33da6e7317878..9ca65e0813e25 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -127,7 +127,7 @@ private static void Divide(Span left, ReadOnlySpan right, Span uint divLo = right.Length > 1 ? right[right.Length - 2] : 0; // We measure the leading zeros of the divisor - int shift = LeadingZeros(divHi); + int shift = BitOperations.LeadingZeroCount(divHi); int backShift = 32 - shift; // And, we make sure the most significant bit is set @@ -264,39 +264,5 @@ private static bool DivideGuessTooBig(ulong q, ulong valHi, uint valLo, return false; } - - private static int LeadingZeros(uint value) - { - if (value == 0) - return 32; - - int count = 0; - if ((value & 0xFFFF0000) == 0) - { - count += 16; - value = value << 16; - } - if ((value & 0xFF000000) == 0) - { - count += 8; - value = value << 8; - } - if ((value & 0xF0000000) == 0) - { - count += 4; - value = value << 4; - } - if ((value & 0xC0000000) == 0) - { - count += 2; - value = value << 2; - } - if ((value & 0x80000000) == 0) - { - count += 1; - } - - return count; - } } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs index 64d2306192027..c9944c1ba95bb 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.GcdInv.cs @@ -272,7 +272,7 @@ private static void ExtractDigits(ReadOnlySpan xBuffer, } // Use all the bits but one, see [hac] 14.58 (ii) - int z = LeadingZeros((uint)xh); + int z = BitOperations.LeadingZeroCount((uint)xh); x = ((xh << 32 + z) | (xm << z) | (xl >> 32 - z)) >> 1; y = ((yh << 32 + z) | (ym << z) | (yl >> 32 - z)) >> 1;