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 7e938de2925ef..d61ea2546b168 100644
--- a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj
+++ b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj
@@ -7,12 +7,12 @@
-
+
@@ -30,5 +30,6 @@
+
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 d27244837a089..671f4af2699be 100644
--- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
+++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Buffers;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
@@ -15,9 +16,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);
-
- internal const int StackallocUInt32Limit = 64;
// 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 +211,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 +224,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 +240,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();
}
@@ -473,67 +472,51 @@ internal BigInteger(int n, uint[]? rgu)
///
/// Constructor used during bit manipulation and arithmetic.
- /// When possible the 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
- /// An array, identical in contents to , for which ownership can be transferred; null if no such array was available.
/// The bool indicating the sign of the value.
- private BigInteger(ReadOnlySpan value, uint[]? valueArray, bool negative)
+ private BigInteger(ReadOnlySpan value, bool negative)
{
- Debug.Assert(valueArray is null || value.SequenceEqual(valueArray));
+ int len;
- // Try to conserve space as much as possible by checking for wasted leading uint entries
- // (sometimes the span has leading zeros from bit manipulation operations & and ^).
- if (!value.IsEmpty && value[^1] == 0)
- {
- int len = value.Length - 1;
- while (len > 0 && value[len - 1] == 0)
- {
- len--;
- }
- value = value.Slice(0, len);
- valueArray = null;
- }
+ // 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 (value.IsEmpty)
+ if (len == 0)
{
this = s_bnZeroInt;
}
- else if (value.Length == 1 && value[0] < kuMaskHighBit)
+ else if (len == 1 && value[0] < kuMaskHighBit)
{
- // Values like (Int32.MaxValue+1) are stored as "0x80000000" and as such cannot be packed into _sign.
+ // 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;
if (_sign == int.MinValue)
{
- // Although Int32.MinValue fits in _sign, we represent this case differently for negate.
+ // Although Int32.MinValue fits in _sign, we represent this case differently for negate
this = s_bnMinInt;
}
}
else
{
_sign = negative ? -1 : +1;
- _bits = valueArray ?? value.ToArray();
+ _bits = value.Slice(0, len).ToArray();
}
-
AssertValid();
}
///
- /// 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);
+ bool isNegative = dwordCount > 0 && ((value[dwordCount - 1] & kuMaskHighBit) == kuMaskHighBit);
- // 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)
@@ -568,18 +551,9 @@ 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();
AssertValid();
return;
}
@@ -608,29 +582,20 @@ 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;
+ _bits = value.Slice(0, len).ToArray();
}
AssertValid();
return;
}
- public static BigInteger Zero => s_bnZeroInt;
+ public static BigInteger Zero { get { return s_bnZeroInt; } }
- public static BigInteger One => s_bnOneInt;
+ public static BigInteger One { get { return s_bnOneInt; } }
- public static BigInteger MinusOne => s_bnMinusOneInt;
+ public static BigInteger MinusOne { get { return s_bnMinusOneInt; } }
public bool IsPowerOfTwo
{
@@ -639,12 +604,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)
{
@@ -774,10 +739,26 @@ 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);
- remainder = dividend._sign < 0 ? -1 * rest : rest;
- return new BigInteger(bits, bits, (dividend._sign < 0) ^ (divisor._sign < 0));
+ uint[]? bitsFromPool = null;
+ int size = dividend._bits.Length;
+ Span quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
+
+ try
+ {
+ // may throw DivideByZeroException
+ BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign), quotient, out rest);
+
+ 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);
@@ -789,11 +770,30 @@ public static BigInteger DivRem(BigInteger dividend, BigInteger divisor, out Big
}
else
{
- uint[] rest;
- uint[] bits = BigIntegerCalculator.Divide(dividend._bits, divisor._bits, out rest);
+ uint[]? remainderFromPool = null;
+ int size = dividend._bits.Length;
+ Span rest = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : remainderFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
- remainder = new BigInteger(rest, rest, dividend._sign < 0);
- return new BigInteger(bits, bits, (dividend._sign < 0) ^ (divisor._sign < 0));
+ uint[]? quotientFromPool = null;
+ size = dividend._bits.Length - divisor._bits.Length + 1;
+ 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);
+
+ remainder = new BigInteger(rest, dividend._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;
}
}
@@ -823,7 +823,7 @@ public static double Log(BigInteger value, double baseValue)
ulong l = value._bits.Length > 2 ? value._bits[value._bits.Length - 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
@@ -857,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, null, negative: false);
+ : new BigInteger(right._bits, negative: false);
}
if (trivialRight)
@@ -865,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, null, negative: false);
+ : new BigInteger(left._bits, negative: false);
}
Debug.Assert(left._bits != null && right._bits != null);
@@ -880,29 +880,46 @@ 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);
+ 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 <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).Slice(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];
+
+ result = BigIntegerCalculator.Gcd(left, right);
+ }
+ else
+ {
+ Span bits = (leftBits.Length <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : bitsFromPool = ArrayPool.Shared.Rent(leftBits.Length)).Slice(0, leftBits.Length);
- return BigIntegerCalculator.Gcd(left, right);
+ BigIntegerCalculator.Gcd(leftBits, rightBits, bits);
+ result = new BigInteger(bits, negative: false);
}
- uint[] bits = BigIntegerCalculator.Gcd(leftBits, rightBits);
- return new BigInteger(bits, bits, negative: false);
+ if (bitsFromPool != null)
+ ArrayPool.Shared.Return(bitsFromPool);
+
+ return result;
}
public static BigInteger Max(BigInteger left, BigInteger right)
@@ -932,6 +949,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)) :
@@ -939,17 +958,43 @@ 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 = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
+ bits.Clear();
+ if (trivialValue)
+ {
+ 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)
+ {
+ 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, bits, value._sign < 0 && !exponent.IsEven);
+ if (bitsFromPool != null)
+ ArrayPool.Shared.Return(bitsFromPool);
}
+
+ return result;
}
public static BigInteger Pow(BigInteger value, int exponent)
@@ -966,6 +1011,10 @@ public static BigInteger Pow(BigInteger value, int exponent)
bool trivialValue = value._bits == null;
+ uint power = NumericsHelpers.Abs(exponent);
+ uint[]? bitsFromPool = null;
+ BigInteger result;
+
if (trivialValue)
{
if (value._sign == 1)
@@ -974,34 +1023,54 @@ 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 = ((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);
+ result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0);
+ }
+ else
+ {
+ int size = BigIntegerCalculator.PowBound(power, value._bits!.Length);
+ 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);
+ result = new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0);
}
- uint[] bits = trivialValue
- ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent))
- : BigIntegerCalculator.Pow(value._bits!, NumericsHelpers.Abs(exponent));
+ if (bitsFromPool != null)
+ ArrayPool.Shared.Return(bitsFromPool);
- return new BigInteger(bits, bits, value._sign < 0 && (exponent & 1) != 0);
+ return result;
}
public override int GetHashCode()
{
AssertValid();
- if (_bits == null)
+ if (_bits is null)
return _sign;
+
int hash = _sign;
for (int iv = _bits.Length; --iv >= 0;)
- hash = NumericsHelpers.CombineHash(hash, unchecked((int)_bits[iv]));
+ 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)
{
AssertValid();
- if (!(obj is BigInteger))
- return false;
- return Equals((BigInteger)obj);
+ return obj is BigInteger other && Equals(other);
}
public bool Equals(long other)
@@ -1124,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);
}
///
@@ -1424,79 +1493,59 @@ 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 using the fewest number of uints possible.
///
- private ReadOnlySpan ToUInt32Span(Span scratch)
+ /// Pre-allocated buffer by the caller.
+ /// The actual number of copied elements.
+ private int WriteTo(Span buffer)
{
- Debug.Assert(!scratch.IsEmpty);
-
- if (_bits == null && _sign == 0)
- {
- scratch[0] = 0;
- return scratch.Slice(0, 1);
- }
+ Debug.Assert(_bits is null || _sign == 0 ? buffer.Length == 2 : buffer.Length >= _bits.Length + 1);
- Span dwords = scratch;
- bool dwordsIsScratch = true;
uint highDWord;
- if (_bits == null)
+ if (_bits is null)
{
- dwords[0] = unchecked((uint)_sign);
- dwords = dwords.Slice(0, 1);
+ buffer[0] = unchecked((uint)_sign);
highDWord = (_sign < 0) ? uint.MaxValue : 0;
}
- else if (_sign == -1)
+ else
{
- if (dwords.Length >= _bits.Length)
+ _bits.CopyTo(buffer);
+ buffer = buffer.Slice(0, _bits.Length + 1);
+ if (_sign == -1)
{
- _bits.AsSpan().CopyTo(dwords);
- dwords = dwords.Slice(0, _bits.Length);
+ NumericsHelpers.DangerousMakeTwosComplement(buffer.Slice(0, buffer.Length - 1)); // Mutates dwords
+ highDWord = uint.MaxValue;
}
else
- {
- dwords = (uint[])_bits.Clone();
- }
- NumericsHelpers.DangerousMakeTwosComplement(dwords); // Mutates dwords
- highDWord = uint.MaxValue;
- }
- else
- {
- dwords = _bits;
- highDWord = 0;
- dwordsIsScratch = false;
+ highDWord = 0;
}
// Find highest significant byte and ensure high bit is 0 if positive, 1 if negative
- int msb;
- for (msb = dwords.Length - 1; msb > 0 && dwords[msb] == highDWord; msb--);
- bool needExtraByte = (dwords[msb] & 0x80000000) != (highDWord & 0x80000000);
-
- int length = msb + 1 + (needExtraByte ? 1 : 0);
- bool copyDwordsToScratch = true;
- if (length <= scratch.Length)
- {
- scratch = scratch.Slice(0, length);
- copyDwordsToScratch = !dwordsIsScratch;
- }
- else
+ int msb = buffer.Length - 2;
+ while (msb > 0 && buffer[msb] == highDWord)
{
- scratch = new uint[length];
+ msb--;
}
- if (copyDwordsToScratch)
- {
- dwords.Slice(0, msb + 1).CopyTo(scratch);
- }
+ // Ensure high bit is 0 if positive, 1 if negative
+ bool needExtraByte = (buffer[msb] & 0x80000000) != (highDWord & 0x80000000);
+ int count;
if (needExtraByte)
{
- scratch[^1] = highDWord;
+ count = msb + 2;
+ buffer = buffer.Slice(0, count);
+ buffer[buffer.Length - 1] = highDWord;
+ }
+ else
+ {
+ count = msb + 1;
+ buffer = buffer.Slice(0, count);
}
- return scratch;
+ return count;
}
public override string ToString()
@@ -1524,42 +1573,72 @@ 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, 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, bits, leftSign < 0);
+ int size = rightBits.Length + 1;
+ 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);
}
+ else if (trivialRight)
+ {
+ Debug.Assert(!leftBits.IsEmpty);
- Debug.Assert(leftBits != null && rightBits != null);
+ int size = leftBits.Length + 1;
+ Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
- if (leftBits.Length < rightBits.Length)
+ BigIntegerCalculator.Add(leftBits, NumericsHelpers.Abs(rightSign), bits);
+ result = new BigInteger(bits, leftSign < 0);
+ }
+ else if (leftBits.Length < rightBits.Length)
{
- uint[] bits = BigIntegerCalculator.Add(rightBits, leftBits);
- return new BigInteger(bits, bits, leftSign < 0);
+ Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty);
+
+ int size = rightBits.Length + 1;
+ 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);
}
else
{
- uint[] bits = BigIntegerCalculator.Add(leftBits, rightBits);
- return new BigInteger(bits, bits, leftSign < 0);
+ Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty);
+
+ int size = leftBits.Length + 1;
+ 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);
}
+
+ if (bitsFromPool != null)
+ ArrayPool.Shared.Return(bitsFromPool);
+
+ return result;
}
public static BigInteger operator -(BigInteger left, BigInteger right)
@@ -1572,42 +1651,70 @@ private static BigInteger Add(uint[]? leftBits, int leftSign, uint[]? rightBits,
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, 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, bits, leftSign < 0);
+ int size = rightBits.Length;
+ 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);
}
+ else if (trivialRight)
+ {
+ Debug.Assert(!leftBits.IsEmpty);
- Debug.Assert(leftBits != null && rightBits != null);
+ int size = leftBits.Length;
+ Span bits = (size <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(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, bits, leftSign >= 0);
+ int size = rightBits.Length;
+ 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);
}
else
{
- uint[] bits = BigIntegerCalculator.Subtract(leftBits, rightBits);
- return new BigInteger(bits, bits, leftSign < 0);
+ Debug.Assert(!leftBits.IsEmpty && !rightBits.IsEmpty);
+
+ int size = leftBits.Length;
+ 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);
}
+
+ if (bitsFromPool != null)
+ ArrayPool.Shared.Return(bitsFromPool);
+
+ return result;
}
public static implicit operator BigInteger(byte value)
@@ -1822,7 +1929,7 @@ public static explicit operator double(BigInteger value)
ulong m = length > 1 ? bits[length - 2] : 0;
ulong l = length > 2 ? bits[length - 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);
@@ -1858,24 +1965,53 @@ 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.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]);
- ReadOnlySpan y = right.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]);
- 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;
+ uint[]? leftBufferFromPool = null;
+ int size = (left._bits?.Length ?? 1) + 1;
+ 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 = ((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[BigIntegerCalculator.StackAllocThreshold]
+ : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
+
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 = ((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);
+
+ if (leftBufferFromPool != null)
+ ArrayPool.Shared.Return(leftBufferFromPool);
+
+ if (rightBufferFromPool != null)
+ ArrayPool.Shared.Return(rightBufferFromPool);
+
+ var result = new BigInteger(z);
+
+ if (resultBufferFromPool != null)
+ ArrayPool.Shared.Return(resultBufferFromPool);
+
+ return result;
}
public static BigInteger operator |(BigInteger left, BigInteger right)
@@ -1885,47 +2021,104 @@ 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.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]);
- ReadOnlySpan y = right.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]);
- 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;
+ uint[]? leftBufferFromPool = null;
+ int size = (left._bits?.Length ?? 1) + 1;
+ 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 = ((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[BigIntegerCalculator.StackAllocThreshold]
+ : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
+
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 = ((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);
+
+ if (leftBufferFromPool != null)
+ ArrayPool.Shared.Return(leftBufferFromPool);
+
+ if (rightBufferFromPool != null)
+ ArrayPool.Shared.Return(rightBufferFromPool);
+
+ var result = new BigInteger(z);
+
+ if (resultBufferFromPool != null)
+ ArrayPool.Shared.Return(resultBufferFromPool);
+
+ return result;
}
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.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]);
- ReadOnlySpan y = right.ToUInt32Span(stackalloc uint[StackallocUInt32Limit / 2]);
- 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;
+ uint[]? leftBufferFromPool = null;
+ int size = (left._bits?.Length ?? 1) + 1;
+ 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 = ((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[BigIntegerCalculator.StackAllocThreshold]
+ : resultBufferFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
+
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 = ((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);
+ if (leftBufferFromPool != null)
+ ArrayPool.Shared.Return(leftBufferFromPool);
+
+ if (rightBufferFromPool != null)
+ ArrayPool.Shared.Return(rightBufferFromPool);
+
+ var result = new BigInteger(z);
+
+ if (resultBufferFromPool != null)
+ ArrayPool.Shared.Return(resultBufferFromPool);
+
+ return result;
}
public static BigInteger operator <<(BigInteger value, int shift)
@@ -1934,28 +2127,26 @@ public static explicit operator decimal(BigInteger value)
return value;
if (shift == int.MinValue)
- return (value >> int.MaxValue) >> 1;
+ return ((value >> int.MaxValue) >> 1);
if (shift < 0)
return value >> -shift;
(int digitShift, int smallShift) = Math.DivRem(shift, kcbitUint);
- Span xd = stackalloc uint[1];
- bool negx = GetPartsForBitManipulation(ref value, ref xd);
+ uint[]? xdFromPool = null;
+ int xl = value._bits?.Length ?? 1;
+ Span xd = (xl <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : xdFromPool = ArrayPool.Shared.Rent(xl)).Slice(0, xl);
+ bool negx = value.GetPartsForBitManipulation(xd);
- int zl = xd.Length + digitShift + 1;
- uint[]? zdArray = null;
- Span zd = stackalloc uint[0];
- if (zl <= StackallocUInt32Limit)
- {
- zd = stackalloc uint[StackallocUInt32Limit].Slice(0, zl);
- zd.Slice(0, digitShift).Clear();
- }
- else
- {
- zd = zdArray = new uint[zl];
- }
+ int zl = xl + digitShift + 1;
+ uint[]? zdFromPool = null;
+ Span zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : zdFromPool = ArrayPool.Shared.Rent(zl)).Slice(0, zl);
+ zd.Clear();
uint carry = 0;
if (smallShift == 0)
@@ -1968,16 +2159,25 @@ public static explicit operator decimal(BigInteger value)
else
{
int carryShift = kcbitUint - smallShift;
- for (int i = 0; i < xd.Length; i++)
+ int i;
+ for (i = 0; i < xd.Length; i++)
{
uint rot = xd[i];
zd[i + digitShift] = rot << smallShift | carry;
carry = rot >> carryShift;
}
}
- zd[^1] = carry;
- return new BigInteger(zd, zdArray, negx);
+ zd[zd.Length - 1] = carry;
+
+ 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)
@@ -1986,38 +2186,30 @@ public static explicit operator decimal(BigInteger value)
return value;
if (shift == int.MinValue)
- return value << int.MaxValue << 1;
+ return ((value << int.MaxValue) << 1);
if (shift < 0)
return value << -shift;
(int digitShift, int smallShift) = Math.DivRem(shift, kcbitUint);
- Span stackallocedXd = stackalloc uint[1];
- Span xd = stackallocedXd;
- bool negx = GetPartsForBitManipulation(ref value, ref xd);
+ BigInteger result;
+
+ uint[]? xdFromPool = null;
+ int xl = value._bits?.Length ?? 1;
+ 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;
if (negx)
{
if (shift >= (kcbitUint * xd.Length))
{
- return MinusOne;
- }
-
- if (xd != stackallocedXd)
- {
- // make a copy of the part extracted from GetPartsForBitManipulation
- if (xd.Length <= StackallocUInt32Limit)
- {
- stackallocedXd = stackalloc uint[StackallocUInt32Limit].Slice(0, xd.Length);
- xd.CopyTo(stackallocedXd);
- xd = stackallocedXd;
- }
- else
- {
- xd = xd.ToArray();
- }
+ result = MinusOne;
+ goto exit;
}
NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd
@@ -2028,18 +2220,15 @@ public static explicit operator decimal(BigInteger value)
// After a 32 bit right shift, it becomes [0x00, 0x00] which is [0x00, 0x00] when converted back.
// The expected result is [0x00, 0x00, 0xFFFFFFFF] (2's complement) or [0x00, 0x00, 0x01] when converted back
// If the 2's component's last element is a 0, we will track the sign externally
- trackSignBit = smallShift == 0 && xd[^1] == 0;
+ trackSignBit = smallShift == 0 && xd[xd.Length - 1] == 0;
}
- int zl = xd.Length - digitShift + (trackSignBit ? 1: 0);
- uint[]? zdArray = null;
- Span zd = stackalloc uint[0];
- if (zl > 0)
- {
- zd = zl <= StackallocUInt32Limit ?
- stackalloc uint[StackallocUInt32Limit].Slice(0, zl) :
- zdArray = new uint[zl];
- }
+ uint[]? zdFromPool = null;
+ int zl = Math.Max(xl - digitShift, 0) + (trackSignBit ? 1 : 0);
+ Span zd = ((uint)zl <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : zdFromPool = ArrayPool.Shared.Rent(zl)).Slice(0, zl);
+ zd.Clear();
if (smallShift == 0)
{
@@ -2063,17 +2252,25 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) :
carry = rot << carryShift;
}
}
+
if (negx)
{
// Set the tracked sign to the last element
if (trackSignBit)
- {
- zd[^1] = 0xFFFFFFFF;
- }
+ zd[zd.Length - 1] = 0xFFFFFFFF;
+
NumericsHelpers.DangerousMakeTwosComplement(zd); // Mutates zd
}
- return new BigInteger(zd, zdArray, 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)
@@ -2118,46 +2315,87 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, 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, 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, bits, (left._sign < 0) ^ (right._sign < 0));
+ int size = right.Length + 1;
+ 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));
}
+ else if (trivialRight)
+ {
+ Debug.Assert(!left.IsEmpty);
- Debug.Assert(left._bits != null && right._bits != null);
+ int size = left.Length + 1;
+ Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
- if (left._bits == right._bits)
- {
- uint[] bits = BigIntegerCalculator.Square(left._bits);
- return new BigInteger(bits, 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 == right)
+ {
+ int size = left.Length + right.Length;
+ Span bits = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : bitsFromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
- if (left._bits.Length < right._bits.Length)
+ BigIntegerCalculator.Square(left, bits);
+ result = new BigInteger(bits, negative: false);
+ }
+ else if (left.Length < right.Length)
{
- uint[] bits = BigIntegerCalculator.Multiply(right._bits, left._bits);
- return new BigInteger(bits, bits, (left._sign < 0) ^ (right._sign < 0));
+ Debug.Assert(!left.IsEmpty && !right.IsEmpty);
+
+ int size = left.Length + right.Length;
+ 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);
+ result = new BigInteger(bits, (leftSign < 0) ^ (rightSign < 0));
}
else
{
- uint[] bits = BigIntegerCalculator.Multiply(left._bits, right._bits);
- return new BigInteger(bits, bits, (left._sign < 0) ^ (right._sign < 0));
+ Debug.Assert(!left.IsEmpty && !right.IsEmpty);
+
+ int size = left.Length + right.Length;
+ 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);
+ 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)
@@ -2180,11 +2418,28 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) :
return s_bnZeroInt;
}
+ uint[]? quotientFromPool = null;
+
if (trivialDivisor)
{
Debug.Assert(dividend._bits != null);
- uint[] bits = BigIntegerCalculator.Divide(dividend._bits, NumericsHelpers.Abs(divisor._sign));
- return new BigInteger(bits, bits, (dividend._sign < 0) ^ (divisor._sign < 0));
+
+ int size = dividend._bits.Length;
+ Span quotient = ((uint)size <= BigIntegerCalculator.StackAllocThreshold ?
+ stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : quotientFromPool = ArrayPool.Shared.Rent(size)).Slice(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);
@@ -2195,8 +2450,18 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) :
}
else
{
- uint[] bits = BigIntegerCalculator.Divide(dividend._bits, divisor._bits);
- return new BigInteger(bits, bits, (dividend._sign < 0) ^ (divisor._sign < 0));
+ int size = dividend._bits.Length - divisor._bits.Length + 1;
+ 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));
+
+ if (quotientFromPool != null)
+ ArrayPool.Shared.Return(quotientFromPool);
+
+ return result;
}
}
@@ -2233,8 +2498,20 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) :
{
return dividend;
}
- uint[] bits = BigIntegerCalculator.Remainder(dividend._bits, divisor._bits);
- return new BigInteger(bits, bits, dividend._sign < 0);
+
+ uint[]? bitsFromPool = null;
+ int size = dividend._bits.Length;
+ 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);
+
+ if (bitsFromPool != null)
+ ArrayPool.Shared.Return(bitsFromPool);
+
+ return result;
}
public static bool operator <(BigInteger left, BigInteger right)
@@ -2451,28 +2728,26 @@ public long GetBitLength()
/// 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 span containing the entire big integer in "large" (denormalized) form.
+ /// 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. Expecting to be passed a writeable span of length 1.
+ /// format for convenience.
///
/// True for negative numbers.
- private static bool GetPartsForBitManipulation(ref BigInteger x, ref Span xd)
+ private bool GetPartsForBitManipulation(Span xd)
{
- Debug.Assert(xd.Length == 1);
+ Debug.Assert(_bits is null ? xd.Length == 1 : xd.Length == _bits.Length);
- if (x._bits == null)
+ if (_bits is null)
{
- xd[0] = (uint)(x._sign < 0 ? -x._sign : x._sign);
+ xd[0] = (uint)(_sign < 0 ? -_sign : _sign);
}
else
{
- xd = x._bits;
+ _bits.CopyTo(xd);
}
-
- return x._sign < 0;
+ return _sign < 0;
}
internal static int GetDiffLength(uint[] rgu1, uint[] rgu2, int cu)
@@ -2506,4 +2781,4 @@ private void AssertValid()
}
}
}
-}
+}
\ No newline at end of file
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 4439d67346de6..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
@@ -2,112 +2,89 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
-using System.Security;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace System.Numerics
{
internal static partial class BigIntegerCalculator
{
- public static uint[] Add(uint[] left, uint right)
+ public static void Add(ReadOnlySpan left, uint right, Span bits)
{
- Debug.Assert(left != null);
Debug.Assert(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
// processing the 32-bit integer, since it's a single element.
- uint[] bits = new uint[left.Length + 1];
+ long carry = right;
- long digit = (long)left[0] + right;
- bits[0] = unchecked((uint)digit);
- long carry = digit >> 32;
-
- for (int i = 1; i < left.Length; i++)
+ for (int i = 0; i < left.Length; i++)
{
- digit = left[i] + carry;
+ long digit = left[i] + carry;
bits[i] = unchecked((uint)digit);
carry = digit >> 32;
}
- bits[left.Length] = (uint)carry;
- return bits;
+ bits[left.Length] = (uint)carry;
}
- public static unsafe uint[] Add(uint[] left, uint[] right)
+ public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span bits)
{
- Debug.Assert(left != null);
- Debug.Assert(right != null);
Debug.Assert(left.Length >= right.Length);
+ Debug.Assert(bits.Length == left.Length + 1);
- // Switching to unsafe 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);
- }
-
- return bits;
- }
+ int i = 0;
+ long carry = 0L;
- private static unsafe void Add(uint* left, int leftLength,
- uint* right, int rightLength,
- uint* bits, int bitsLength)
- {
- Debug.Assert(leftLength >= 0);
- Debug.Assert(rightLength >= 0);
- Debug.Assert(leftLength >= rightLength);
- Debug.Assert(bitsLength == leftLength + 1);
+ // 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 < rightLength; 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 < leftLength; i++)
+ 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 unsafe void AddSelf(uint* left, int leftLength,
- 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 >= right.Length);
+
+ int i = 0;
+ long carry = 0L;
+
+ // Switching to managed references helps eliminating
+ // index bounds check...
+ ref uint leftPtr = ref MemoryMarshal.GetReference(left);
// 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 < rightLength; i++)
+ for ( ; i < right.Length; i++)
{
- long digit = (left[i] + carry) + right[i];
- left[i] = 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 < leftLength; i++)
+ for ( ; carry != 0 && i < left.Length; i++)
{
long digit = left[i] + carry;
left[i] = (uint)digit;
@@ -117,110 +94,84 @@ private static unsafe void AddSelf(uint* left, int leftLength,
Debug.Assert(carry == 0);
}
- public static uint[] Subtract(uint[] left, uint right)
+ public static void Subtract(ReadOnlySpan left, uint right, Span bits)
{
- Debug.Assert(left != null);
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;
- long digit = (long)left[0] - right;
- bits[0] = unchecked((uint)digit);
- long carry = digit >> 32;
-
- for (int i = 1; i < left.Length; i++)
+ for (int i = 0; i < left.Length; i++)
{
- digit = left[i] + carry;
+ long digit = left[i] + carry;
bits[i] = unchecked((uint)digit);
carry = digit >> 32;
}
-
- return bits;
}
- public static unsafe uint[] Subtract(uint[] left, uint[] right)
+ public static void Subtract(ReadOnlySpan left, ReadOnlySpan right, Span bits)
{
- Debug.Assert(left != null);
- Debug.Assert(right != null);
Debug.Assert(left.Length >= right.Length);
Debug.Assert(Compare(left, right) >= 0);
+ Debug.Assert(bits.Length == left.Length);
- // Switching to unsafe 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);
- }
-
- return bits;
- }
+ int i = 0;
+ long carry = 0L;
- private static unsafe void Subtract(uint* left, int leftLength,
- uint* right, int rightLength,
- 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(bitsLength == leftLength);
+ // 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 < rightLength; 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 < leftLength; 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;
}
Debug.Assert(carry == 0);
}
- private static unsafe void SubtractSelf(uint* left, int leftLength,
- 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(left, leftLength, right, rightLength) >= 0);
+ 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);
// 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 < rightLength; i++)
+ for (; i < right.Length; i++)
{
- long digit = (left[i] + carry) - right[i];
- left[i] = 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 < leftLength; i++)
+ for (; carry != 0 && i < left.Length; i++)
{
long digit = left[i] + carry;
left[i] = (uint)digit;
@@ -229,48 +180,5 @@ private static unsafe void SubtractSelf(uint* left, int leftLength,
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 unsafe int Compare(uint* left, int leftLength,
- 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 (left[i] < right[i])
- return -1;
- if (left[i] > right[i])
- return 1;
- }
-
- return 0;
- }
}
}
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 ec05118f03007..0000000000000
--- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics;
-using System.Security;
-
-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 unsafe 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.
-
- fixed (uint* b = _bits, v = value._bits, t = temp._bits)
- {
- 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);
- }
- }
-
- Apply(ref temp, _length + value._length);
- }
-
- public unsafe 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.
-
- fixed (uint* b = _bits, t = temp._bits)
- {
- Square(b, _length,
- t, _length + _length);
- }
-
- Apply(ref temp, _length + _length);
- }
-
- public void Reduce(ref 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);
- }
-
- public unsafe 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)
- {
- fixed (uint* b = _bits, m = modulus)
- {
- Divide(b, _length,
- m, modulus.Length,
- null, 0);
- }
-
- _length = ActualLength(_bits, modulus.Length);
- }
- }
-
- public unsafe 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)
- {
- fixed (uint* b = _bits, m = modulus._bits)
- {
- Divide(b, _length,
- m, modulus._length,
- null, 0);
- }
-
- _length = ActualLength(_bits, 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;
- }
-
- 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(_bits, 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(_bits, maxLength);
- }
- }
- }
-}
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 bb75e77a228d0..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
@@ -1,26 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Buffers;
using System.Diagnostics;
-using System.Security;
namespace System.Numerics
{
internal static partial class BigIntegerCalculator
{
- public static uint[] Divide(uint[] left, uint right,
- out uint remainder)
+ public static void Divide(ReadOnlySpan left, uint right, Span quotient, out uint remainder)
{
- Debug.Assert(left != null);
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--)
{
ulong value = (carry << 32) | left[i];
@@ -29,19 +27,15 @@ public static uint[] Divide(uint[] left, uint right,
carry = value - digit * right;
}
remainder = (uint)carry;
-
- return quotient;
}
- public static uint[] Divide(uint[] left, uint right)
+ public static void Divide(ReadOnlySpan left, uint right, Span quotient)
{
- Debug.Assert(left != null);
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--)
{
@@ -50,17 +44,13 @@ public static uint[] Divide(uint[] left, uint right)
quotient[i] = (uint)digit;
carry = value - digit * right;
}
-
- return quotient;
}
- 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.
-
ulong carry = 0UL;
for (int i = left.Length - 1; i >= 0; i--)
{
@@ -71,115 +61,79 @@ public static uint Remainder(uint[] left, uint right)
return (uint)carry;
}
- public static unsafe uint[] Divide(uint[] left, uint[] right,
- out uint[] remainder)
+ public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Span quotient, Span 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);
+ Debug.Assert(quotient.Length == left.Length - right.Length + 1);
+ Debug.Assert(remainder.Length == left.Length);
- // Switching to unsafe pointers helps sparing
- // some nasty index calculations...
-
- uint[] localLeft = left.AsSpan().ToArray(); // left will get overwritten, we need a local copy
- 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);
- }
-
- remainder = localLeft;
-
- return bits;
+ left.CopyTo(remainder);
+ Divide(remainder, right, quotient);
}
- public static unsafe uint[] Divide(uint[] left, uint[] right)
+ public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Span quotient)
{
- Debug.Assert(left != null);
- Debug.Assert(right != null);
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);
// Same as above, but only returning the quotient.
- // left will get overwritten, we need a local copy
- Span localLeft = stackalloc uint[0];
- if (left.Length <= BigInteger.StackallocUInt32Limit)
- {
- localLeft = stackalloc uint[BigInteger.StackallocUInt32Limit].Slice(0, left.Length);
- left.AsSpan().CopyTo(localLeft);
- }
- else
- {
- localLeft = left.AsSpan().ToArray();
- }
+ uint[]? leftCopyFromPool = null;
- uint[] bits = new uint[left.Length - right.Length + 1];
+ // 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[StackAllocThreshold]
+ : leftCopyFromPool = ArrayPool.Shared.Rent(left.Length)).Slice(0, left.Length);
+ left.CopyTo(leftCopy);
- fixed (uint* l = &localLeft[0], r = &right[0], b = &bits[0])
- {
- Divide(l, localLeft.Length,
- r, right.Length,
- b, bits.Length);
- }
+ Divide(leftCopy, right, quotient);
- return bits;
+ if (leftCopyFromPool != null)
+ ArrayPool.Shared.Return(leftCopyFromPool);
}
- public static unsafe uint[] Remainder(uint[] left, uint[] right)
+ public static void Remainder(ReadOnlySpan left, ReadOnlySpan right, Span 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);
+ Debug.Assert(remainder.Length >= left.Length);
// Same as above, but only returning the remainder.
- uint[] localLeft = left.AsSpan().ToArray(); // left will get overwritten, we need a local copy
-
- fixed (uint* l = &localLeft[0], r = &right[0])
- {
- Divide(l, localLeft.Length,
- r, right.Length,
- null, 0);
- }
-
- return localLeft;
+ left.CopyTo(remainder);
+ Divide(remainder, right, default);
}
- private static unsafe void Divide(uint* left, int leftLength,
- uint* right, int rightLength,
- 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 = right[rightLength - 1];
- uint divLo = rightLength > 1 ? right[rightLength - 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);
+ int shift = BitOperations.LeadingZeroCount(divHi);
int backShift = 32 - shift;
// And, we make sure the most significant bit is set
if (shift > 0)
{
- uint divNx = rightLength > 2 ? right[rightLength - 3] : 0;
+ uint divNx = right.Length > 2 ? right[right.Length - 3] : 0;
divHi = (divHi << shift) | (divLo >> backShift);
divLo = (divLo << shift) | (divNx >> backShift);
@@ -187,10 +141,10 @@ private static unsafe void Divide(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 ? left[i] : 0;
+ int n = i - right.Length;
+ uint t = (uint)i < (uint)left.Length ? left[i] : 0;
ulong valHi = ((ulong)t << 32) | left[i - 1];
uint valLo = i > 1 ? left[i - 2] : 0;
@@ -217,15 +171,13 @@ 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(left.Slice(n), right, 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(left.Slice(n), right);
--digit;
Debug.Assert(carry == 1);
@@ -233,41 +185,36 @@ private static unsafe void Divide(uint* left, int leftLength,
}
// We have the digit!
- if (bitsLength != 0)
+ if ((uint)n < (uint)bits.Length)
bits[n] = (uint)digit;
- if (i < leftLength)
+
+ if ((uint)i < (uint)left.Length)
left[i] = 0;
}
}
- private static unsafe uint AddDivisor(uint* left, int leftLength,
- 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 >= 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++)
{
- ulong digit = (left[i] + carry) + right[i];
- left[i] = unchecked((uint)digit);
+ ref uint leftElement = ref left[i];
+ ulong digit = (leftElement + carry) + 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,
- 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 >= right.Length);
Debug.Assert(q <= 0xFFFFFFFF);
// Combines a subtract and a multiply operation, which is naturally
@@ -275,14 +222,15 @@ private static unsafe uint SubtractDivisor(uint* left, int leftLength,
ulong carry = 0UL;
- for (int i = 0; i < rightLength; i++)
+ for (int i = 0; i < right.Length; i++)
{
carry += right[i] * q;
uint digit = unchecked((uint)carry);
carry = carry >> 32;
- if (left[i] < digit)
+ ref uint leftElement = ref left[i];
+ if (leftElement < digit)
++carry;
- left[i] = unchecked(left[i] - digit);
+ leftElement = unchecked(leftElement - digit);
}
return (uint)carry;
@@ -316,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.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs
index 8731794b40299..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
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
-using System.Security;
namespace System.Numerics
{
@@ -14,135 +13,114 @@ internal static partial class BigIntegerCalculator
// see https://en.wikipedia.org/wiki/Barrett_reduction
- internal readonly struct FastReducer
+ private 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);
+ var length = SubMod(value, _q2.Slice(0, l2), _modulus, _modulus.Length + 1);
+ value = value.Slice(length);
+ value.Clear();
+
+ return length;
}
- private static unsafe 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(!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);
-
- if (leftLength > k)
+ if (left.Length > k)
{
- leftLength -= k;
+ left = left.Slice(k);
- fixed (uint* l = left, r = right, b = bits)
+ if (left.Length < right.Length)
{
- if (leftLength < rightLength)
- {
- Multiply(r, rightLength,
- l + k, leftLength,
- b, leftLength + rightLength);
- }
- else
- {
- Multiply(l + k, leftLength,
- r, rightLength,
- b, leftLength + rightLength);
- }
+ Multiply(right,
+ left,
+ bits.Slice(0, left.Length + right.Length));
+ }
+ else
+ {
+ Multiply(left,
+ right,
+ bits.Slice(0, left.Length + right.Length));
}
- return ActualLength(bits, leftLength + rightLength);
+ return ActualLength(bits.Slice(0, left.Length + right.Length));
}
return 0;
}
- private static unsafe 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);
-
// 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);
- fixed (uint* l = left, r = right, m = modulus)
- {
- SubtractSelf(l, leftLength, r, rightLength);
- leftLength = ActualLength(left, leftLength);
+ SubtractSelf(left, right);
+ left = left.Slice(0, ActualLength(left));
- while (Compare(l, leftLength, m, modulus.Length) >= 0)
- {
- SubtractSelf(l, leftLength, m, modulus.Length);
- leftLength = ActualLength(left, leftLength);
- }
+ while (Compare(left, modulus) >= 0)
+ {
+ SubtractSelf(left, modulus);
+ left = left.Slice(0, ActualLength(left));
}
- Array.Clear(left, leftLength, left.Length - leftLength);
-
- return leftLength;
+ return left.Length;
}
}
}
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 d984912ec6246..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
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Buffers;
using System.Diagnostics;
namespace System.Numerics
@@ -40,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);
@@ -54,27 +54,34 @@ public static uint Gcd(uint[] 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 <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : rightCopyFromPool = ArrayPool.Shared.Rent(right.Length)).Slice(0, right.Length);
+ right.CopyTo(rightCopy);
- return leftBuffer.GetBits();
+ Gcd(result, rightCopy);
+
+ if (rightCopyFromPool != null)
+ ArrayPool.Shared.Return(rightCopyFromPool);
}
- private static void Gcd(ref BitsBuffer left, ref BitsBuffer right)
+ private static void 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);
+
+ 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.
@@ -83,11 +90,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(ref left, ref right, out x, out y);
+ ExtractDigits(left, right, out x, out y);
uint a = 1U, b = 0U;
uint c = 0U, d = 1U;
@@ -149,85 +156,112 @@ 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;
}
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)
{
// 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];
- ulong x = ((ulong)xBits[1] << 32) | xBits[0];
- ulong y = ((ulong)yBits[1] << 32) | yBits[0];
+ left = left.Slice(0, Overwrite(left, Gcd(x, y)));
+ Overwrite(right, 0U);
+ }
+
+ left.CopyTo(result);
+ }
- left.Overwrite(Gcd(x, y));
- right.Overwrite(0);
+ private static int Overwrite(Span buffer, ulong value)
+ {
+ Debug.Assert(buffer.Length >= 2);
+
+ if (buffer.Length > 2)
+ {
+ // Ensure leading zeros in little-endian
+ buffer.Slice(2).Clear();
}
+
+ uint lo = unchecked((uint)value);
+ uint hi = (uint)(value >> 32);
+
+ buffer[1] = hi;
+ buffer[0] = lo;
+ return hi != 0 ? 2 : lo != 0 ? 1 : 0;
}
- private static void ExtractDigits(ref BitsBuffer xBuffer,
- ref BitsBuffer yBuffer,
+ private static int Overwrite(Span bits, uint value)
+ {
+ Debug.Assert(bits.Length >= 1);
+
+ if (bits.Length > 1)
+ {
+ // Ensure leading zeros in little-endian
+ bits.Slice(1).Clear();
+ }
+
+ bits[0] = value;
+ return value != 0 ? 1 : 0;
+ }
+
+ 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:
@@ -238,7 +272,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;
@@ -246,23 +280,20 @@ private static void ExtractDigits(ref BitsBuffer xBuffer,
Debug.Assert(x >= y);
}
- private static void LehmerCore(ref BitsBuffer xBuffer,
- ref BitsBuffer yBuffer,
- 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(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++)
@@ -275,8 +306,20 @@ private static void LehmerCore(ref BitsBuffer xBuffer,
y[i] = unchecked((uint)yDigit);
}
- xBuffer.Refresh(length);
- yBuffer.Refresh(length);
+ return length;
+ }
+
+ private static int Refresh(Span bits, int maxLength)
+ {
+ Debug.Assert(bits.Length >= maxLength);
+
+ if (bits.Length > maxLength)
+ {
+ // Ensure leading zeros
+ bits.Slice(maxLength).Clear();
+ }
+
+ return ActualLength(bits.Slice(0, maxLength));
}
}
}
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 c646173430275..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
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Buffers;
using System.Diagnostics;
namespace System.Numerics
@@ -12,50 +13,104 @@ 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.
+ Pow(value != 0U ? stackalloc uint[1] { value } : default, power, bits);
+ }
- int size = PowBound(power, 1, 1);
- BitsBuffer v = new BitsBuffer(size, value);
- return PowCore(power, ref v);
+ 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[StackAllocThreshold]
+ : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length);
+ temp.Clear();
+
+ uint[]? valueCopyFromPool = null;
+ 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();
+
+ PowCore(valueCopy, value.Length, temp, power, bits).CopyTo(bits);
+
+ if (tempFromPool != null)
+ ArrayPool.Shared.Return(tempFromPool);
+ if (valueCopyFromPool != null)
+ ArrayPool.Shared.Return(valueCopyFromPool);
}
- public static uint[] Pow(uint[] value, uint power)
+ private static Span PowCore(Span value, int valueLength, Span temp, uint power, Span result)
{
- Debug.Assert(value != null);
+ Debug.Assert(value.Length >= valueLength);
+ Debug.Assert(temp.Length == result.Length);
+ Debug.Assert(value.Length == temp.Length);
+
+ result[0] = 1;
+ int bitsLength = 1;
- // The basic pow method for a big integer.
- // To spare memory allocations we first roughly
- // estimate an upper bound for our buffers.
+ // The basic pow algorithm using square-and-multiply.
+ while (power != 0)
+ {
+ if ((power & 1) == 1)
+ bitsLength = MultiplySelf(ref result, bitsLength, value.Slice(0, valueLength), ref temp);
+ if (power != 1)
+ valueLength = SquareSelf(ref value, valueLength, ref temp);
+ power = power >> 1;
+ }
- int size = PowBound(power, value.Length, 1);
- BitsBuffer v = new BitsBuffer(size, value);
- return PowCore(power, ref v);
+ return 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));
+ }
+
+ left.Clear();
+ //switch buffers
+ Span t = left;
+ left = temp;
+ temp = t;
+ return ActualLength(left.Slice(0, resultLength));
+ }
- PowCore(power, ref value, ref result, ref temp);
+ private static int SquareSelf(ref Span value, int valueLength, ref Span temp)
+ {
+ Debug.Assert(valueLength <= value.Length);
+ Debug.Assert(temp.Length >= valueLength + valueLength);
- return result.GetBits();
+ 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,
- int resultLength)
+ public 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
@@ -71,64 +126,41 @@ 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
// raised by a 32-bit integer...
- return PowCore(power, modulus, value, 1);
+ return PowCore(value, power, modulus, 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...
uint v = Remainder(value, modulus);
- return PowCore(power, modulus, v, 1);
+ return PowCore(v, power, modulus, 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);
+ return PowCore(value, power, modulus, 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...
uint v = Remainder(value, modulus);
- return PowCore(power, modulus, v, 1);
+ return PowCore(v, power, modulus, 1);
}
- private static uint PowCore(uint[] 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.
@@ -145,11 +177,10 @@ private static uint PowCore(uint[] 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.
@@ -166,116 +197,221 @@ 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 ? stackalloc uint[1] { value } : 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 <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : valueCopyFromPool = ArrayPool.Shared.Rent(size)).Slice(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 <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(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 ? stackalloc uint[1] { value } : 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 <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : valueCopyFromPool = ArrayPool.Shared.Rent(size)).Slice(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 <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : tempFromPool = ArrayPool.Shared.Rent(bits.Length)).Slice(0, bits.Length);
+ temp.Clear();
+
+ PowCore(valueCopy, ActualLength(valueCopy), power, modulus, 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);
}
+#if DEBUG
// Mutable for unit testing...
- private static int ReducerThreshold = 32;
-
- private static uint[] PowCore(uint[] power, uint[] modulus,
- ref BitsBuffer value)
+ private static
+#else
+ private const
+#endif
+ int ReducerThreshold = 32;
+
+ private static void PowCore(Span value, int valueLength,
+ ReadOnlySpan 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(value, valueLength, power, modulus, bits, 1, temp).CopyTo(bits);
}
else
{
- FastReducer reducer = new FastReducer(modulus);
- PowCore(power, ref reducer, ref value, ref result, ref temp);
+ int size = modulus.Length * 2 + 1;
+ uint[]? rFromPool = null;
+ 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 = ((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 = ((uint)size <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : q1FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
+ q1.Clear();
+
+ uint[]? q2FromPool = null;
+ 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);
+
+ if (rFromPool != null)
+ ArrayPool.Shared.Return(rFromPool);
+
+ PowCore(value, valueLength, power, reducer, bits, 1, temp).CopyTo(bits);
+
+ if (muFromPool != null)
+ ArrayPool.Shared.Return(muFromPool);
+ if (q1FromPool != null)
+ ArrayPool.Shared.Return(q1FromPool);
+ 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(value, valueLength, power, modulus, bits, 1, temp).CopyTo(bits);
}
else
{
- FastReducer reducer = new FastReducer(modulus);
- PowCore(power, ref reducer, ref value, ref result, ref temp);
+ int size = modulus.Length * 2 + 1;
+ uint[]? rFromPool = null;
+ 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 = ((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 = ((uint)size <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : q1FromPool = ArrayPool.Shared.Rent(size)).Slice(0, size);
+ q1.Clear();
+
+ uint[]? q2FromPool = null;
+ 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);
+
+ if (rFromPool != null)
+ ArrayPool.Shared.Return(rFromPool);
+
+ PowCore(value, valueLength, power, reducer, bits, 1, temp).CopyTo(bits);
+
+ if (muFromPool != null)
+ ArrayPool.Shared.Return(muFromPool);
+ if (q1FromPool != null)
+ ArrayPool.Shared.Return(q1FromPool);
+ 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(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.
@@ -290,22 +426,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(value, valueLength, power[power.Length - 1], modulus, result, resultLength, temp);
}
- private static void PowCore(uint power, uint[] modulus,
- ref BitsBuffer value, ref BitsBuffer result,
- ref BitsBuffer 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.
@@ -317,21 +453,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.Slice(0, resultLength);
}
- private static void PowCore(uint[] power, ref FastReducer reducer,
- ref BitsBuffer value, ref BitsBuffer result,
- ref BitsBuffer 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.
@@ -346,22 +485,22 @@ private static void PowCore(uint[] power, ref FastReducer reducer,
{
if ((p & 1) == 1)
{
- result.MultiplySelf(ref value, ref temp);
- result.Reduce(ref 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(ref reducer);
+ valueLength = SquareSelf(ref value, valueLength, ref temp);
+ valueLength = reducer.Reduce(value.Slice(0, valueLength));
p = p >> 1;
}
}
- PowCore(power[power.Length - 1], ref reducer, ref value, ref result,
- ref temp);
+ return PowCore(value, valueLength, power[power.Length - 1], reducer, result, resultLength, temp);
}
- private static void PowCore(uint power, ref FastReducer reducer,
- ref BitsBuffer value, ref BitsBuffer result,
- ref BitsBuffer 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.
@@ -373,34 +512,18 @@ private static void PowCore(uint power, ref FastReducer reducer,
{
if ((power & 1) == 1)
{
- result.MultiplySelf(ref value, ref temp);
- result.Reduce(ref 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(ref reducer);
+ valueLength = SquareSelf(ref value, valueLength, ref temp);
+ valueLength = reducer.Reduce(value.Slice(0, valueLength));
}
power = power >> 1;
}
- }
-
- private static int ActualLength(uint[] 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);
- while (length > 0 && value[length - 1] == 0)
- --length;
- return length;
+ return result;
}
}
}
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 f77d02f37ccb3..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
@@ -1,40 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Buffers;
using System.Diagnostics;
-using System.Security;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace System.Numerics
{
internal static partial class BigIntegerCalculator
{
- public static unsafe uint[] Square(uint[] value)
- {
- Debug.Assert(value != null);
-
- // Switching to unsafe pointers helps sparing
- // some nasty index calculations...
-
- uint[] bits = new uint[value.Length + value.Length];
-
- fixed (uint* v = value, b = bits)
- {
- Square(v, value.Length,
- b, bits.Length);
- }
-
- return bits;
- }
-
+#if DEBUG
// Mutable for unit testing...
- private static int SquareThreshold = 32;
- private static int AllocationThreshold = 256;
+ private static
+#else
+ private const
+#endif
+ int SquareThreshold = 32;
- private static unsafe void Square(uint* value, int valueLength,
- uint* bits, int bitsLength)
+ public static void Square(ReadOnlySpan value, Span bits)
{
- Debug.Assert(valueLength >= 0);
- Debug.Assert(bitsLength == valueLength + valueLength);
+ 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
@@ -45,8 +31,12 @@ private static unsafe void Square(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)
{
+ // Switching to managed references helps eliminating
+ // index bounds check...
+ 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
@@ -58,20 +48,20 @@ 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.
-
- for (int i = 0; i < valueLength; i++)
+ for (int i = 0; i < value.Length; i++)
{
ulong carry = 0UL;
+ uint v = value[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)value[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
@@ -88,81 +78,59 @@ private static unsafe void Square(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
- uint* valueLow = value;
- int valueLowLength = n;
- uint* valueHigh = value + n;
- int valueHighLength = valueLength - n;
+ ReadOnlySpan valueLow = value.Slice(0, n);
+ ReadOnlySpan valueHigh = value.Slice(n);
// ... prepare our result array (to reuse its memory)
- uint* bitsLow = bits;
- int bitsLowLength = n2;
- uint* bitsHigh = bits + n2;
- int bitsHighLength = bitsLength - n2;
+ Span bitsLow = bits.Slice(0, n2);
+ Span bitsHigh = bits.Slice(n2);
// ... compute z_0 = a_0 * a_0 (squaring again!)
- Square(valueLow, valueLowLength,
- bitsLow, bitsLowLength);
+ Square(valueLow, bitsLow);
// ... compute z_2 = a_1 * a_1 (squaring again!)
- Square(valueHigh, valueHighLength,
- bitsHigh, bitsHighLength);
+ Square(valueHigh, bitsHigh);
+
+ int foldLength = valueHigh.Length + 1;
+ uint[]? foldFromPool = null;
+ Span fold = ((uint)foldLength <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : foldFromPool = ArrayPool.Shared.Rent(foldLength)).Slice(0, foldLength);
+ fold.Clear();
- int foldLength = valueHighLength + 1;
int coreLength = foldLength + foldLength;
+ uint[]? coreFromPool = null;
+ Span core = ((uint)coreLength <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : coreFromPool = ArrayPool.Shared.Rent(coreLength)).Slice(0, coreLength);
+ core.Clear();
- 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
- {
- 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);
- }
- }
+ // ... compute z_a = a_1 + a_0 (call it fold...)
+ Add(valueHigh, valueLow, fold);
+
+ // ... compute z_1 = z_a * z_a - z_0 - z_2
+ Square(fold, core);
+
+ if (foldFromPool != null)
+ ArrayPool.Shared.Return(foldFromPool);
+
+ SubtractCore(bitsHigh, bitsLow, core);
+
+ // ... and finally merge the result! :-)
+ AddSelf(bits.Slice(n), core);
+
+ if (coreFromPool != null)
+ ArrayPool.Shared.Return(coreFromPool);
}
}
- public static uint[] Multiply(uint[] left, uint right)
+ public static void Multiply(ReadOnlySpan left, uint right, Span bits)
{
- Debug.Assert(left != null);
+ 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
@@ -171,51 +139,28 @@ public static uint[] Multiply(uint[] left, uint right)
int i = 0;
ulong carry = 0UL;
- uint[] bits = new uint[left.Length + 1];
- for (; i < left.Length; i++)
+ for ( ; i < left.Length; i++)
{
ulong digits = (ulong)left[i] * right + carry;
bits[i] = unchecked((uint)digits);
carry = digits >> 32;
}
bits[i] = (uint)carry;
-
- return bits;
- }
-
- public static unsafe uint[] Multiply(uint[] left, uint[] 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];
-
- fixed (uint* l = left, r = right, b = bits)
- {
- Multiply(l, left.Length,
- r, right.Length,
- b, bits.Length);
- }
-
- return bits;
}
+#if DEBUG
// Mutable for unit testing...
- private static int MultiplyThreshold = 32;
+ private static
+#else
+ private const
+#endif
+ int MultiplyThreshold = 32;
- private static unsafe void Multiply(uint* left, int leftLength,
- uint* right, int rightLength,
- uint* bits, int bitsLength)
+ public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Span bits)
{
- Debug.Assert(leftLength >= 0);
- Debug.Assert(rightLength >= 0);
- Debug.Assert(leftLength >= rightLength);
- Debug.Assert(bitsLength == leftLength + rightLength);
+ 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
@@ -226,8 +171,12 @@ private static unsafe void Multiply(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)
{
+ // Switching to managed references helps eliminating
+ // index bounds check...
+ 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...
@@ -235,17 +184,17 @@ 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!).
- for (int i = 0; i < rightLength; i++)
+ 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++)
{
- ulong digits = bits[i + j] + carry
- + (ulong)left[j] * right[i];
- bits[i + j] = unchecked((uint)digits);
+ ref uint elementPtr = ref Unsafe.Add(ref resultPtr, i + j);
+ ulong digits = elementPtr + carry + (ulong)left[j] * right[i];
+ elementPtr = unchecked((uint)digits);
carry = digits >> 32;
}
- bits[i + leftLength] = (uint)carry;
+ Unsafe.Add(ref resultPtr, i + left.Length) = (uint)carry;
}
}
else
@@ -262,111 +211,77 @@ private static unsafe void Multiply(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
- uint* leftLow = left;
- int leftLowLength = n;
- uint* leftHigh = left + n;
- int leftHighLength = leftLength - n;
+ ReadOnlySpan leftLow = left.Slice(0, n);
+ ReadOnlySpan leftHigh = left.Slice(n);
// ... split right like b = (b_1 << n) + b_0
- uint* rightLow = right;
- int rightLowLength = n;
- uint* rightHigh = right + n;
- int rightHighLength = rightLength - n;
+ ReadOnlySpan rightLow = right.Slice(0, n);
+ ReadOnlySpan rightHigh = right.Slice(n);
// ... prepare our result array (to reuse its memory)
- uint* bitsLow = bits;
- int bitsLowLength = n2;
- uint* bitsHigh = bits + n2;
- int bitsHighLength = bitsLength - n2;
+ Span bitsLow = bits.Slice(0, n2);
+ Span bitsHigh = bits.Slice(n2);
// ... compute z_0 = a_0 * b_0 (multiply again)
- Multiply(leftLow, leftLowLength,
- rightLow, rightLowLength,
- bitsLow, bitsLowLength);
+ Multiply(leftLow, rightLow, bitsLow);
// ... compute z_2 = a_1 * b_1 (multiply again)
- Multiply(leftHigh, leftHighLength,
- rightHigh, rightHighLength,
- bitsHigh, bitsHighLength);
+ Multiply(leftHigh, rightHigh, bitsHigh);
+
+ int leftFoldLength = leftHigh.Length + 1;
+ uint[]? leftFoldFromPool = null;
+ 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 = ((uint)rightFoldLength <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : rightFoldFromPool = ArrayPool.Shared.Rent(rightFoldLength)).Slice(0, rightFoldLength);
+ rightFold.Clear();
- int leftFoldLength = leftHighLength + 1;
- int rightFoldLength = rightHighLength + 1;
int coreLength = leftFoldLength + rightFoldLength;
+ uint[]? coreFromPool = null;
+ Span core = ((uint)coreLength <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : coreFromPool = ArrayPool.Shared.Rent(coreLength)).Slice(0, coreLength);
+ core.Clear();
- if (coreLength < AllocationThreshold)
- {
- 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);
- }
- }
+ // ... compute z_a = a_1 + a_0 (call it fold...)
+ Add(leftHigh, leftLow, leftFold);
+
+ // ... compute z_b = b_1 + b_0 (call it fold...)
+ Add(rightHigh, rightLow, rightFold);
+
+ // ... compute z_1 = z_a * z_b - z_0 - z_2
+ Multiply(leftFold, rightFold, core);
+
+ if (leftFoldFromPool != null)
+ ArrayPool.Shared.Return(leftFoldFromPool);
+
+ if (rightFoldFromPool != null)
+ ArrayPool.Shared.Return(rightFoldFromPool);
+
+ SubtractCore(bitsHigh, bitsLow, core);
+
+ // ... and finally merge the result! :-)
+ AddSelf(bits.Slice(n), core);
+
+ if (coreFromPool != null)
+ ArrayPool.Shared.Return(coreFromPool);
}
}
- private static unsafe void SubtractCore(uint* left, int leftLength,
- uint* right, int rightLength,
- 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 >= 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,
@@ -378,19 +293,26 @@ private static unsafe void SubtractCore(uint* left, int leftLength,
int i = 0;
long carry = 0L;
- for (; i < rightLength; i++)
+ // Switching to managed references helps eliminating
+ // index bounds check...
+ ref uint leftPtr = ref MemoryMarshal.GetReference(left);
+ ref uint corePtr = ref MemoryMarshal.GetReference(core);
+
+ for ( ; i < right.Length; i++)
{
- long digit = (core[i] + carry) - left[i] - right[i];
- core[i] = unchecked((uint)digit);
+ 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 < leftLength; i++)
+
+ for ( ; i < left.Length; i++)
{
- long digit = (core[i] + carry) - left[i];
- core[i] = unchecked((uint)digit);
+ 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 < coreLength; i++)
+
+ for ( ; carry != 0 && i < core.Length; i++)
{
long digit = core[i] + carry;
core[i] = (uint)digit;
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..37d4ae7596568
--- /dev/null
+++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Numerics
+{
+ internal static partial class BigIntegerCalculator
+ {
+#if DEBUG
+ // Mutable for unit testing...
+ internal static
+#else
+ internal const
+#endif
+ int StackAllocThreshold = 64;
+
+ 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 = left[i];
+ uint rightElement = right[i];
+ if (leftElement < rightElement)
+ return -1;
+ if (leftElement > rightElement)
+ return 1;
+ }
+
+ 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;
+ }
+
+ 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
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 5ce7919e830bf..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 <= BigInteger.StackallocUInt32Limit)
- ? stackalloc uint[blockCount]
- : MemoryMarshal.Cast((arrayFromPool = ArrayPool.Shared.Rent(blockCount)).AsSpan(0, blockCount));
+ Span