From 1d59ab4ca854acf3e4818e5b56f6651ed5c7b4eb Mon Sep 17 00:00:00 2001 From: axelheer Date: Fri, 5 Jun 2015 08:32:33 +0200 Subject: [PATCH] Improve performance of BigInteger.Pow/ModPow - To introduce further performance tweaks, the exponentiation algorithms are rewritten and ported to `BigIntegerCalculator`. - To scale better for bigger numbers a `FastReducer` is in use instead of "ordinary" modulo operations, which is based on multiplications. - Furthermore the newly introduced `FastReducer` triggers a bad corner case within the division algorithm, which gets fixed too. - A performance regression at 64 bits within integer division was found, which gets fixed too (no more allocations within that code). - The test code for threshold values of square / multiply / modpow now modifies these thresholds for more thorough testing. --- .../src/System.Runtime.Numerics.csproj | 3 + .../src/System/Numerics/BigInteger.cs | 202 ++------- .../Numerics/BigIntegerCalculator.AddSub.cs | 1 + .../BigIntegerCalculator.BitsBuffer.cs | 147 +++++++ .../Numerics/BigIntegerCalculator.DivRem.cs | 322 +++++++------- .../BigIntegerCalculator.FastReducer.cs | 148 +++++++ .../Numerics/BigIntegerCalculator.PowMod.cs | 401 ++++++++++++++++++ .../Numerics/BigIntegerCalculator.SquMul.cs | 14 +- .../tests/BigInteger/BigIntTools.cs | 23 + .../tests/BigInteger/modpow.cs | 88 ++-- .../tests/BigInteger/multiply.cs | 8 + 11 files changed, 987 insertions(+), 370 deletions(-) create mode 100644 src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs create mode 100644 src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs create mode 100644 src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs diff --git a/src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj b/src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj index 510832050ef6..456b62aa0a14 100644 --- a/src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj +++ b/src/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj @@ -19,7 +19,10 @@ + + + diff --git a/src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 6005819fbec3..ec2b07f8251b 100644 --- a/src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1049,45 +1049,6 @@ public static BigInteger Min(BigInteger left, BigInteger right) return right; } - - private static void ModPowUpdateResult(ref BigIntegerBuilder regRes, ref BigIntegerBuilder regVal, ref BigIntegerBuilder regMod, ref BigIntegerBuilder regTmp) - { - NumericsHelpers.Swap(ref regRes, ref regTmp); - regRes.Mul(ref regTmp, ref regVal); // result = result * value; - regRes.Mod(ref regMod); // result = result % modulus; - } - - private static void ModPowSquareModValue(ref BigIntegerBuilder regVal, ref BigIntegerBuilder regMod, ref BigIntegerBuilder regTmp) - { - NumericsHelpers.Swap(ref regVal, ref regTmp); - regVal.Mul(ref regTmp, ref regTmp); // value = value * value; - regVal.Mod(ref regMod); // value = value % modulus; - } - - private static void ModPowInner(uint exp, ref BigIntegerBuilder regRes, ref BigIntegerBuilder regVal, ref BigIntegerBuilder regMod, ref BigIntegerBuilder regTmp) - { - while (exp != 0) // !(Exponent.IsZero) - { - if ((exp & 1) == 1) // !(Exponent.IsEven) - ModPowUpdateResult(ref regRes, ref regVal, ref regMod, ref regTmp); - if (exp == 1) // Exponent.IsOne - we can exit early - break; - ModPowSquareModValue(ref regVal, ref regMod, ref regTmp); - exp >>= 1; - } - } - - private static void ModPowInner32(uint exp, ref BigIntegerBuilder regRes, ref BigIntegerBuilder regVal, ref BigIntegerBuilder regMod, ref BigIntegerBuilder regTmp) - { - for (int i = 0; i < 32; i++) - { - if ((exp & 1) == 1) // !(Exponent.IsEven) - ModPowUpdateResult(ref regRes, ref regVal, ref regMod, ref regTmp); - ModPowSquareModValue(ref regVal, ref regMod, ref regTmp); - exp >>= 1; - } - } - public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigInteger modulus) { if (exponent.Sign < 0) @@ -1098,36 +1059,31 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege exponent.AssertValid(); modulus.AssertValid(); - int signRes = +1; - int signVal = +1; - int signMod = +1; - bool expIsEven = exponent.IsEven; - BigIntegerBuilder regRes = new BigIntegerBuilder(BigInteger.One, ref signRes); - BigIntegerBuilder regVal = new BigIntegerBuilder(value, ref signVal); - BigIntegerBuilder regMod = new BigIntegerBuilder(modulus, ref signMod); - BigIntegerBuilder regTmp = new BigIntegerBuilder(regVal.Size); + bool trivialValue = value._bits == null; + bool trivialExponent = exponent._bits == null; + bool trivialModulus = modulus._bits == null; - regRes.Mod(ref regMod); // Handle special case of exponent=0, modulus=1 + if (trivialModulus) + { + long bits = trivialValue && trivialExponent ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) : + trivialValue ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), exponent._bits, NumericsHelpers.Abs(modulus._sign)) : + trivialExponent ? BigIntegerCalculator.Pow(value._bits, NumericsHelpers.Abs(exponent._sign), NumericsHelpers.Abs(modulus._sign)) : + BigIntegerCalculator.Pow(value._bits, exponent._bits, NumericsHelpers.Abs(modulus._sign)); - if (exponent._bits == null) - { // exponent fits into an Int32 - ModPowInner((uint)exponent._sign, ref regRes, ref regVal, ref regMod, ref regTmp); + return value._sign < 0 && !exponent.IsEven ? -1 * bits : bits; } else - { // very large exponent - int len = Length(exponent._bits); - for (int i = 0; i < len - 1; i++) - { - uint exp = exponent._bits[i]; - ModPowInner32(exp, ref regRes, ref regVal, ref regMod, ref regTmp); - } - ModPowInner(exponent._bits[len - 1], ref regRes, ref regVal, ref regMod, ref regTmp); - } + { + 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); - return regRes.GetInteger(value._sign > 0 ? +1 : expIsEven ? +1 : -1); + return new BigInteger(bits, value._sign < 0 && !exponent.IsEven); + } } - public static BigInteger Pow(BigInteger value, Int32 exponent) + public static BigInteger Pow(BigInteger value, int exponent) { if (exponent < 0) throw new ArgumentOutOfRangeException("exponent", SR.ArgumentOutOfRange_MustBeNonNeg); @@ -1136,75 +1092,27 @@ public static BigInteger Pow(BigInteger value, Int32 exponent) value.AssertValid(); if (exponent == 0) - return BigInteger.One; + return s_bnOneInt; if (exponent == 1) return value; - if (value._bits == null) + + bool trivialValue = value._bits == null; + + if (trivialValue) { if (value._sign == 1) return value; if (value._sign == -1) - return (exponent & 1) != 0 ? value : 1; + return (exponent & 1) != 0 ? value : s_bnOneInt; if (value._sign == 0) return value; } - int sign = +1; - BigIntegerBuilder regSquare = new BigIntegerBuilder(value, ref sign); - - // Get an estimate of the size needed for regSquare and regRes, so we can minimize allocations. - int cuSquareMin = regSquare.Size; - int cuSquareMax = cuSquareMin; - uint uSquareMin = regSquare.High; - uint uSquareMax = uSquareMin + 1; - if (uSquareMax == 0) - { - cuSquareMax++; - uSquareMax = 1; - } - int cuResMin = 1; - int cuResMax = 1; - uint uResMin = 1; - uint uResMax = 1; - - for (int expTmp = exponent; ;) - { - if ((expTmp & 1) != 0) - { - MulUpper(ref uResMax, ref cuResMax, uSquareMax, cuSquareMax); - MulLower(ref uResMin, ref cuResMin, uSquareMin, cuSquareMin); - } - - if ((expTmp >>= 1) == 0) - break; - - MulUpper(ref uSquareMax, ref cuSquareMax, uSquareMax, cuSquareMax); - MulLower(ref uSquareMin, ref cuSquareMin, uSquareMin, cuSquareMin); - } - - if (cuResMax > 1) - regSquare.EnsureWritable(cuResMax, 0); - BigIntegerBuilder regTmp = new BigIntegerBuilder(cuResMax); - BigIntegerBuilder regRes = new BigIntegerBuilder(cuResMax); - regRes.Set(1); - - if ((exponent & 1) == 0) - sign = +1; - - for (int expTmp = exponent; ;) - { - if ((expTmp & 1) != 0) - { - NumericsHelpers.Swap(ref regRes, ref regTmp); - regRes.Mul(ref regSquare, ref regTmp); - } - if ((expTmp >>= 1) == 0) - break; - NumericsHelpers.Swap(ref regSquare, ref regTmp); - regSquare.Mul(ref regTmp, ref regTmp); - } + uint[] bits = trivialValue + ? BigIntegerCalculator.Pow(NumericsHelpers.Abs(value._sign), NumericsHelpers.Abs(exponent)) + : BigIntegerCalculator.Pow(value._bits, NumericsHelpers.Abs(exponent)); - return regRes.GetInteger(sign); + return new BigInteger(bits, value._sign < 0 && (exponent & 1) != 0); } #endregion public static methods @@ -1810,8 +1718,8 @@ private static BigInteger Subtract(uint[] leftBits, int leftSign, uint[] rightBi if (trivialDivisor) { - uint[] bits = BigIntegerCalculator.Remainder(dividend._bits, NumericsHelpers.Abs(divisor._sign)); - return new BigInteger(bits, dividend._sign < 0); + long bits = BigIntegerCalculator.Remainder(dividend._bits, NumericsHelpers.Abs(divisor._sign)); + return dividend._sign < 0 ? -1 * bits : bits; } if (dividend._bits.Length < divisor._bits.Length) @@ -2032,11 +1940,10 @@ private void SetBitsFromDouble(Double value) [Pure] internal static int Length(uint[] rgu) { - int cu = rgu.Length; - if (rgu[cu - 1] != 0) - return cu; - Debug.Assert(cu >= 2 && rgu[cu - 2] != 0); - return cu - 1; + Debug.Assert(rgu[rgu.Length - 1] != 0); + + // no leading zeros + return rgu.Length; } internal int _Sign { get { return _sign; } } @@ -2088,47 +1995,6 @@ private static bool GetPartsForBitManipulation(ref BigInteger x, out uint[] xd, return x._sign < 0; } - // Gets an upper bound on the product of uHiRes * (2^32)^(cuRes-1) and uHiMul * (2^32)^(cuMul-1). - // The result is put int uHiRes and cuRes. - private static void MulUpper(ref uint uHiRes, ref int cuRes, uint uHiMul, int cuMul) - { - ulong uu = (ulong)uHiRes * uHiMul; - uint uHi = NumericsHelpers.GetHi(uu); - if (uHi != 0) - { - if (NumericsHelpers.GetLo(uu) != 0 && ++uHi == 0) - { - uHi = 1; - cuRes++; - } - uHiRes = uHi; - cuRes += cuMul; - } - else - { - uHiRes = NumericsHelpers.GetLo(uu); - cuRes += cuMul - 1; - } - } - - // Gets a lower bound on the product of uHiRes * (2^32)^(cuRes-1) and uHiMul * (2^32)^(cuMul-1). - // The result is put int uHiRes and cuRes. - private static void MulLower(ref uint uHiRes, ref int cuRes, uint uHiMul, int cuMul) - { - ulong uu = (ulong)uHiRes * uHiMul; - uint uHi = NumericsHelpers.GetHi(uu); - if (uHi != 0) - { - uHiRes = uHi; - cuRes += cuMul; - } - else - { - uHiRes = NumericsHelpers.GetLo(uu); - cuRes += cuMul - 1; - } - } - internal static int GetDiffLength(uint[] rgu1, uint[] rgu2, int cu) { for (int iv = cu; --iv >= 0;) diff --git a/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs index b7b5055c354d..7b7942df9bf9 100644 --- a/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs +++ b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.AddSub.cs @@ -197,6 +197,7 @@ private unsafe static void Subtract(uint* left, int leftLength, bits[i] = (uint)digit; carry = digit >> 32; } + Debug.Assert(carry == 0); } diff --git a/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs new file mode 100644 index 000000000000..fea2132e1021 --- /dev/null +++ b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.BitsBuffer.cs @@ -0,0 +1,147 @@ +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, 0, _bits, 0, _length); + } + + [SecuritySafeCritical] + 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); + } + + [SecuritySafeCritical] + 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); + } + + [SecuritySafeCritical] + 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 uint[] GetBits() + { + return _bits; + } + + public int GetSize() + { + return _bits.Length; + } + + 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/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs index de148e4decd0..5f53db9bcfbf 100644 --- a/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs +++ b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.DivRem.cs @@ -49,7 +49,7 @@ public static uint[] Divide(uint[] left, uint right) return quotient; } - public static uint[] Remainder(uint[] left, uint right) + public static uint Remainder(uint[] left, uint right) { Debug.Assert(left != null); Debug.Assert(left.Length >= 1); @@ -63,7 +63,7 @@ public static uint[] Remainder(uint[] left, uint right) carry = value % right; } - return new uint[] { (uint)carry }; + return (uint)carry; } [SecuritySafeCritical] @@ -79,18 +79,21 @@ public unsafe static uint[] Divide(uint[] left, uint[] right, // Switching to unsafe pointers helps sparing // some nasty index calculations... - uint[] quotient = new uint[left.Length - right.Length + 1]; - remainder = new uint[right.Length]; + // NOTE: left will get overwritten, we need a local copy - fixed(uint* l = left, r = right, q = quotient, e = remainder) + uint[] localLeft = CreateCopy(left); + uint[] bits = new uint[left.Length - right.Length + 1]; + + fixed (uint* l = localLeft, r = right, b = bits) { - Divide(l, left.Length, + Divide(l, localLeft.Length, r, right.Length, - q, quotient.Length, - e, remainder.Length); + b, bits.Length); } - return quotient; + remainder = localLeft; + + return bits; } [SecuritySafeCritical] @@ -104,17 +107,19 @@ public unsafe static uint[] Divide(uint[] left, uint[] right) // Same as above, but only returning the quotient. - uint[] quotient = new uint[left.Length - right.Length + 1]; + // NOTE: left will get overwritten, we need a local copy + + uint[] localLeft = CreateCopy(left); + uint[] bits = new uint[left.Length - right.Length + 1]; - fixed (uint* l = left, r = right, q = quotient) + fixed (uint* l = localLeft, r = right, b = bits) { - Divide(l, left.Length, + Divide(l, localLeft.Length, r, right.Length, - q, quotient.Length, - null, 0); + b, bits.Length); } - return quotient; + return bits; } [SecuritySafeCritical] @@ -128,203 +133,194 @@ public unsafe static uint[] Remainder(uint[] left, uint[] right) // Same as above, but only returning the remainder. - uint[] remainder = new uint[right.Length]; + // NOTE: left will get overwritten, we need a local copy - fixed (uint* l = left, r = right, e = remainder) + uint[] localLeft = CreateCopy(left); + + fixed (uint* l = localLeft, r = right) { - Divide(l, left.Length, + Divide(l, localLeft.Length, r, right.Length, - null, 0, - e, remainder.Length); + null, 0); } - return remainder; + return localLeft; } [SecuritySafeCritical] private unsafe static void Divide(uint* left, int leftLength, uint* right, int rightLength, - uint* quotient, int quotientLength, - uint* remainder, int remainderLength) + uint* bits, int bitsLength) { Debug.Assert(leftLength >= 1); Debug.Assert(rightLength >= 1); Debug.Assert(leftLength >= rightLength); - Debug.Assert(quotientLength == leftLength - rightLength + 1 - || quotientLength == 0); - Debug.Assert(remainderLength == rightLength - || remainderLength == 0); + Debug.Assert(bitsLength == leftLength - rightLength + 1 + || bitsLength == 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. - int dividendLength = leftLength + 1; - int divisorLength = rightLength; - fixed (uint* dividend = new uint[dividendLength], - divisor = new uint[divisorLength], - guess = new uint[divisorLength + 1]) + uint divHi = right[rightLength - 1]; + uint divLo = rightLength > 1 ? right[rightLength - 2] : 0; + + // We measure the leading zeros of the divisor + int shift = LeadingZeros(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; + + divHi = (divHi << shift) | (divLo >> backShift); + divLo = (divLo << shift) | (divNx >> backShift); + } + + // 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--) { - // This will create private copies of left and right, so we can - // modify the actual values of the dividend during computation - // without breaking immutability of the calling structure! - int shift = LeadingZeros(right[rightLength - 1]); - LeftShift(left, leftLength, dividend, dividendLength, shift); - LeftShift(right, rightLength, divisor, divisorLength, shift); - - // Measure dividend again; maybe there aren't any additional - // bits resulting of our shift above to the left? - if (dividend[dividendLength - 1] == 0) - --dividendLength; - - // These values will come in handy - uint divHi = divisor[divisorLength - 1]; - uint divLo = divisorLength > 1 ? divisor[divisorLength - 2] : 0; - int guessLength = 0; - int delta = 0; - - // First, we subtract the divisor until our dividend is smaller, - // if we shift the divisor so they have equal length. This will - // ensure that the highest digit of the dividend is smaller or - // equal to the highest digit of the divisor... - do + int n = i - rightLength; + uint t = i < leftLength ? left[i] : 0; + + ulong valHi = ((ulong)t << 32) | left[i - 1]; + uint valLo = i > 1 ? left[i - 2] : 0; + + // We shifted the divisor, we shift the dividend too + if (shift > 0) { - int n = dividendLength - divisorLength; - delta = Compare(dividend + n, divisorLength, - divisor, divisorLength); - if (delta >= 0) - { - if (quotientLength != 0) - ++quotient[n]; - SubtractSelf(dividend + n, divisorLength, - divisor, divisorLength); - } + uint valNx = i > 2 ? left[i - 3] : 0; + + valHi = (valHi << shift) | (valLo >> backShift); + valLo = (valLo << shift) | (valNx >> backShift); } - while (delta > 0); - // Then, we divide the rest of the bits as we would do it using - // pen and paper: guessing the next digit, subtracting, ... - for (int i = dividendLength - 1; i >= divisorLength; i--) + // First guess for the current digit of the quotient, + // which naturally must have only 32 bits... + ulong digit = valHi / divHi; + if (digit > 0xFFFFFFFF) + digit = 0xFFFFFFFF; + + // Our first guess may be a little bit to big + while (DivideGuessTooBig(digit, valHi, valLo, divHi, divLo)) + --digit; + + if (digit > 0) { - int n = i - divisorLength; - - // First guess for the current digit of the quotient, - // which naturally must have only 32 bits... - ulong valHi = ((ulong)dividend[i] << 32) | dividend[i - 1]; - ulong digit = valHi / divHi; - if (digit > 0xFFFFFFFF) - digit = 0xFFFFFFFF; - - // Our first guess may be a little bit to big - ulong check = divHi * digit + ((divLo * digit) >> 32); - if (check > valHi) + // Now it's time to subtract our current quotient + uint carry = SubtractDivisor(left + n, leftLength - n, + right, rightLength, digit); + if (carry != t) + { + Debug.Assert(carry == t + 1); + + // Our guess was still exactly one too high + carry = AddDivisor(left + n, leftLength - n, + right, rightLength); --digit; - // Our guess may be still a little bit to big - do - { - MultiplyDivisor(divisor, divisorLength, digit, guess); - guessLength = guess[divisorLength] == 0 - ? divisorLength : divisorLength + 1; - delta = Compare(dividend + n, guessLength, - guess, guessLength); - if (delta < 0) - --digit; + Debug.Assert(carry == 1); } - while (delta < 0); - - // We have the digit! - SubtractSelf(dividend + n, guessLength, - guess, guessLength); - if (quotientLength != 0) - quotient[n] = (uint)digit; } - if (remainderLength != 0) - { - // Repairing the remaining dividend gets the remainder - RightShift(dividend, divisorLength, - remainder, remainderLength, - shift); - } + // We have the digit! + if (bitsLength != 0) + bits[n] = (uint)digit; + if (i < leftLength) + left[i] = 0; } } [SecuritySafeCritical] - private unsafe static void LeftShift(uint* value, int valueLength, - uint* target, int targetLength, - int shift) + private unsafe static uint AddDivisor(uint* left, int leftLength, + uint* right, int rightLength) { - Debug.Assert(valueLength >= 1); - Debug.Assert(targetLength == valueLength || - targetLength == valueLength + 1); - Debug.Assert(shift >= 0 && shift < 32); + Debug.Assert(leftLength >= 0); + Debug.Assert(rightLength >= 0); + Debug.Assert(leftLength >= rightLength); - if (shift > 0) - { - int backShift = 32 - shift; - target[0] = value[0] << shift; - for (int i = 1; i < valueLength; i++) - { - target[i] = (value[i] << shift) - | (value[i - 1] >> backShift); - } - if (targetLength > valueLength) - { - target[valueLength] = - value[valueLength - 1] >> backShift; - } - } - else + // Repairs the dividend, if the last subtract was too much + + ulong carry = 0L; + + for (int i = 0; i < rightLength; i++) { - for (int i = 0; i < valueLength; i++) - target[i] = value[i]; + ulong digit = (left[i] + carry) + right[i]; + left[i] = (uint)digit; + carry = digit >> 32; } + + return (uint)carry; } [SecuritySafeCritical] - private unsafe static void RightShift(uint* value, int valueLength, - uint* target, int targetLength, - int shift) + private unsafe static uint SubtractDivisor(uint* left, int leftLength, + uint* right, int rightLength, + ulong q) { - Debug.Assert(valueLength >= 1); - Debug.Assert(targetLength == valueLength); - Debug.Assert(shift >= 0 && shift < 32); + Debug.Assert(leftLength >= 0); + Debug.Assert(rightLength >= 0); + Debug.Assert(leftLength >= rightLength); + Debug.Assert(q <= 0xFFFFFFFF); - if (shift > 0) - { - int backShift = 32 - shift; - for (int i = 0; i < valueLength - 1; i++) - { - target[i] = (value[i] >> shift) - | (value[i + 1] << backShift); - } - target[valueLength - 1] = - (value[valueLength - 1] >> shift); - } - else + // Combines a subtract and a multiply operation, which is naturally + // more efficient than multiplying and then subtracting... + + ulong carry = 0L; + + for (int i = 0; i < rightLength; i++) { - for (int i = 0; i < valueLength; i++) - target[i] = value[i]; + carry += right[i] * q; + uint digit = (uint)carry; + carry = carry >> 32; + if (left[i] < digit) + ++carry; + left[i] -= digit; } + + return (uint)carry; } - [SecuritySafeCritical] - private unsafe static void MultiplyDivisor(uint* left, int leftLength, - ulong right, uint* bits) + private static bool DivideGuessTooBig(ulong q, ulong valHi, uint valLo, + uint divHi, uint divLo) { - Debug.Assert(leftLength >= 1); - Debug.Assert(right <= 0xFFFFFFFF); + Debug.Assert(q <= 0xFFFFFFFF); - ulong carry = 0UL; - for (int i = 0; i < leftLength; i++) - { - ulong digits = left[i] * right + carry; - bits[i] = (uint)digits; - carry = digits >> 32; - } - bits[leftLength] = (uint)carry; + // We multiply the two most significant limbs of the divisor + // with the current guess for the quotient. If those are bigger + // than the three most significant limbs of the current dividend + // we return true, which means the current guess is still too big. + + ulong chkHi = divHi * q; + ulong chkLo = divLo * q; + + chkHi = chkHi + (chkLo >> 32); + chkLo = chkLo & 0xFFFFFFFF; + + if (chkHi < valHi) + return false; + if (chkHi > valHi) + return true; + + if (chkLo < valLo) + return false; + if (chkLo > valLo) + return true; + + return false; + } + + private static uint[] CreateCopy(uint[] value) + { + Debug.Assert(value != null); + Debug.Assert(value.Length != 0); + + var bits = new uint[value.Length]; + Array.Copy(value, 0, bits, 0, bits.Length); + return bits; } private static int LeadingZeros(uint value) diff --git a/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs new file mode 100644 index 000000000000..69a933481b64 --- /dev/null +++ b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -0,0 +1,148 @@ +using System.Diagnostics; +using System.Security; + +namespace System.Numerics +{ + internal static partial class BigIntegerCalculator + { + // If we need to reduce by a certain modulus again and again, it's much + // more efficient to do this with multiplication operations. This is + // possible, if we do some pre-computations first... + + // see https://en.wikipedia.org/wiki/Barrett_reduction + + internal struct FastReducer + { + private readonly uint[] _modulus; + private readonly uint[] _mu; + private readonly uint[] _q1; + private readonly uint[] _q2; + + private readonly int _muLength; + + public FastReducer(uint[] modulus) + { + Debug.Assert(modulus != null); + + // 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); + _modulus = modulus; + + // Allocate memory for quotients once + _q1 = new uint[modulus.Length * 2 + 2]; + _q2 = new uint[modulus.Length * 2 + 1]; + + _muLength = ActualLength(_mu); + } + + public int Reduce(uint[] value, int length) + { + 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; + + // Let q1 = v/2^(k-1) * mu + int l1 = DivMul(value, length, _mu, _muLength, + _q1, _modulus.Length - 1); + + // Let q2 = q1/2^(k+1) * m + int l2 = DivMul(_q1, l1, _modulus, _modulus.Length, + _q2, _modulus.Length + 1); + + // Let v = (v - q2) % 2^(k+1) - i*m + return SubMod(value, length, _q2, l2, + _modulus, _modulus.Length + 1); + } + + [SecuritySafeCritical] + private unsafe static int DivMul(uint[] left, int leftLength, + uint[] right, int rightLength, + uint[] 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); + + // Executes the multiplication algorithm for left and right, + // but skips the first k limbs of left, which is equivalent to + // preceding division by 2^(32*k). To spare memory allocations + // we write the result to an already allocated memory. + + Array.Clear(bits, 0, bits.Length); + + if (leftLength > k) + { + leftLength -= k; + + fixed (uint* l = left, r = right, b = bits) + { + if (leftLength < rightLength) + { + Multiply(r, rightLength, + l + k, leftLength, + b, leftLength + rightLength); + } + else + { + Multiply(l + k, leftLength, + r, rightLength, + b, leftLength + rightLength); + } + } + + return ActualLength(bits, leftLength + rightLength); + } + + return 0; + } + + [SecuritySafeCritical] + private unsafe static int SubMod(uint[] left, int leftLength, + uint[] right, int rightLength, + uint[] 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; + + fixed (uint* l = left, r = right, m = modulus) + { + SubtractSelf(l, leftLength, r, rightLength); + leftLength = ActualLength(left, leftLength); + + while (Compare(l, leftLength, m, modulus.Length) >= 0) + { + SubtractSelf(l, leftLength, m, modulus.Length); + leftLength = ActualLength(left, leftLength); + } + } + + Array.Clear(left, leftLength, left.Length - leftLength); + + return leftLength; + } + } + } +} diff --git a/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs new file mode 100644 index 000000000000..e1db94dee182 --- /dev/null +++ b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.PowMod.cs @@ -0,0 +1,401 @@ +using System.Diagnostics; +using System.Security; + +namespace System.Numerics +{ + internal static partial class BigIntegerCalculator + { + // Executes different exponentiation algorithms, which are + // basically based on the classic square-and-multiply method. + + // https://en.wikipedia.org/wiki/Exponentiation_by_squaring + + public static uint[] Pow(uint value, uint power) + { + // The basic pow method for a 32-bit integer. + // To spare memory allocations we first roughly + // estimate an upper bound for our buffers. + + int size = PowBound(power, 1, 1); + BitsBuffer v = new BitsBuffer(size, value); + return PowCore(power, ref v); + } + + public static uint[] Pow(uint[] value, uint power) + { + Debug.Assert(value != null); + + // The basic pow method for a big integer. + // To spare memory allocations we first roughly + // estimate an upper bound for our buffers. + + int size = PowBound(power, value.Length, 1); + BitsBuffer v = new BitsBuffer(size, value); + return PowCore(power, ref v); + } + + private static uint[] PowCore(uint power, ref BitsBuffer value) + { + // Executes the basic pow algorithm. + + int size = value.GetSize(); + + BitsBuffer temp = new BitsBuffer(size, 0); + BitsBuffer result = new BitsBuffer(size, 1); + + PowCore(power, ref value, ref result, ref temp); + + return result.GetBits(); + } + + private static int PowBound(uint power, int valueLength, + int resultLength) + { + // The basic pow algorithm, but instead of squaring + // and multiplying we just sum up the lengths. + + while (power != 0) + { + if ((power & 1) == 1) + resultLength += valueLength; + if (power != 1) + valueLength += valueLength; + power = power >> 1; + } + + 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); + } + + public static uint Pow(uint[] 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); + } + + public static uint Pow(uint value, uint[] power, uint modulus) + { + Debug.Assert(power != null); + + // The 32-bit modulus pow method for a 32-bit integer + // raised by a big integer... + + return PowCore(power, modulus, value, 1); + } + + public static uint Pow(uint[] value, uint[] power, uint modulus) + { + 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); + } + + private static uint PowCore(uint[] power, uint modulus, + ulong value, ulong result) + { + // The 32-bit modulus pow algorithm for all but + // the last power limb using square-and-multiply. + + for (int i = 0; i < power.Length - 1; i++) + { + uint p = power[i]; + for (int j = 0; j < 32; j++) + { + if ((p & 1) == 1) + result = (result * value) % modulus; + value = (value * value) % modulus; + p = p >> 1; + } + } + + return PowCore(power[power.Length - 1], modulus, value, result); + } + + private static uint PowCore(uint power, uint modulus, + ulong value, ulong result) + { + // The 32-bit modulus pow algorithm for the last or + // the only power limb using square-and-multiply. + + while (power != 0) + { + if ((power & 1) == 1) + result = (result * value) % modulus; + if (power != 1) + value = (value * value) % modulus; + power = power >> 1; + } + + return (uint)(result % modulus); + } + + public static uint[] Pow(uint value, uint power, uint[] modulus) + { + 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); + } + + public static uint[] Pow(uint[] value, uint power, uint[] modulus) + { + Debug.Assert(value != null); + Debug.Assert(modulus != null); + + // The big modulus pow method for a big integer + // raised by a 32-bit integer... + + if (value.Length > modulus.Length) + value = Remainder(value, modulus); + + int size = modulus.Length + modulus.Length; + BitsBuffer v = new BitsBuffer(size, value); + return PowCore(power, modulus, ref v); + } + + public static uint[] Pow(uint value, uint[] power, uint[] modulus) + { + Debug.Assert(power != null); + Debug.Assert(modulus != null); + + // The big modulus pow method for a 32-bit integer + // raised by a big integer... + + int size = modulus.Length + modulus.Length; + BitsBuffer v = new BitsBuffer(size, value); + return PowCore(power, modulus, ref v); + } + + public static uint[] Pow(uint[] value, uint[] power, uint[] modulus) + { + Debug.Assert(value != null); + Debug.Assert(power != null); + Debug.Assert(modulus != null); + + // The big modulus pow method for a big integer + // raised by a big integer... + + if (value.Length > modulus.Length) + value = Remainder(value, modulus); + + int size = modulus.Length + modulus.Length; + BitsBuffer v = new BitsBuffer(size, value); + return PowCore(power, modulus, ref v); + } + + // Mutable for unit testing... + private static int ReducerThreshold = 32; + + private static uint[] PowCore(uint[] power, uint[] modulus, + ref BitsBuffer value) + { + // Executes the big pow algorithm. + + int size = value.GetSize(); + + BitsBuffer temp = new BitsBuffer(size, 0); + BitsBuffer result = new BitsBuffer(size, 1); + + if (modulus.Length < ReducerThreshold) + { + PowCore(power, modulus, ref value, ref result, ref temp); + } + else + { + FastReducer reducer = new FastReducer(modulus); + PowCore(power, ref reducer, ref value, ref result, ref temp); + } + + return result.GetBits(); + } + + private static uint[] PowCore(uint power, uint[] modulus, + ref BitsBuffer value) + { + // Executes the big pow algorithm. + + int size = value.GetSize(); + + BitsBuffer temp = new BitsBuffer(size, 0); + BitsBuffer result = new BitsBuffer(size, 1); + + if (modulus.Length < ReducerThreshold) + { + PowCore(power, modulus, ref value, ref result, ref temp); + } + else + { + FastReducer reducer = new FastReducer(modulus); + PowCore(power, ref reducer, ref value, ref result, ref temp); + } + + return result.GetBits(); + } + + private static void PowCore(uint[] power, uint[] modulus, + ref BitsBuffer value, ref BitsBuffer result, + ref BitsBuffer temp) + { + // The big modulus pow algorithm for all but + // the last power limb using square-and-multiply. + + // NOTE: we're using an ordinary remainder here, + // since the reducer overhead doesn't pay off. + + for (int i = 0; i < power.Length - 1; i++) + { + uint p = power[i]; + for (int j = 0; j < 32; j++) + { + if ((p & 1) == 1) + { + result.MultiplySelf(ref value, ref temp); + result.Reduce(modulus); + } + value.SquareSelf(ref temp); + value.Reduce(modulus); + p = p >> 1; + } + } + + PowCore(power[power.Length - 1], modulus, ref value, ref result, + ref temp); + } + + private static void PowCore(uint power, uint[] modulus, + ref BitsBuffer value, ref BitsBuffer result, + ref BitsBuffer temp) + { + // The big modulus pow algorithm for the last or + // the only power limb using square-and-multiply. + + // NOTE: we're using an ordinary remainder here, + // since the reducer overhead doesn't pay off. + + while (power != 0) + { + if ((power & 1) == 1) + { + result.MultiplySelf(ref value, ref temp); + result.Reduce(modulus); + } + if (power != 1) + { + value.SquareSelf(ref temp); + value.Reduce(modulus); + } + power = power >> 1; + } + } + + private static void PowCore(uint[] power, ref FastReducer reducer, + ref BitsBuffer value, ref BitsBuffer result, + ref BitsBuffer temp) + { + // The big modulus pow algorithm for all but + // the last power limb using square-and-multiply. + + // NOTE: we're using a special reducer here, + // since it's additional overhead does pay off. + + for (int i = 0; i < power.Length - 1; i++) + { + uint p = power[i]; + for (int j = 0; j < 32; j++) + { + if ((p & 1) == 1) + { + result.MultiplySelf(ref value, ref temp); + result.Reduce(ref reducer); + } + value.SquareSelf(ref temp); + value.Reduce(ref reducer); + p = p >> 1; + } + } + + PowCore(power[power.Length - 1], ref reducer, ref value, ref result, + ref temp); + } + + private static void PowCore(uint power, ref FastReducer reducer, + ref BitsBuffer value, ref BitsBuffer result, + ref BitsBuffer temp) + { + // The big modulus pow algorithm for the last or + // the only power limb using square-and-multiply. + + // NOTE: we're using a special reducer here, + // since it's additional overhead does pay off. + + while (power != 0) + { + if ((power & 1) == 1) + { + result.MultiplySelf(ref value, ref temp); + result.Reduce(ref reducer); + } + if (power != 1) + { + value.SquareSelf(ref temp); + value.Reduce(ref reducer); + } + 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; + } + } +} diff --git a/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs index 326815229966..7781b07ceeb9 100644 --- a/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs +++ b/src/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.SquMul.cs @@ -24,11 +24,8 @@ public unsafe static uint[] Square(uint[] value) return bits; } -#if DEBUG - private const int SquareThreshold = 8; -#else - private const int SquareThreshold = 32; -#endif + // Mutable for unit testing... + private static int SquareThreshold = 32; [SecuritySafeCritical] private unsafe static void Square(uint* value, int valueLength, @@ -181,11 +178,8 @@ public unsafe static uint[] Multiply(uint[] left, uint[] right) return bits; } -#if DEBUG - private const int MultiplyThreshold = 8; -#else - private const int MultiplyThreshold = 32; -#endif + // Mutable for unit testing... + private static int MultiplyThreshold = 32; [SecuritySafeCritical] private unsafe static void Multiply(uint* left, int leftLength, diff --git a/src/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs b/src/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs index 659e1952a87f..aac7882c08fe 100644 --- a/src/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs +++ b/src/System.Runtime.Numerics/tests/BigInteger/BigIntTools.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Numerics; +using System.Reflection; using System.Text; namespace BigIntTools @@ -31,6 +33,27 @@ public static string BuildRandomNumber(int maxdigits, int seed) return randNum.ToString().Substring(0, numDigits); } } + + private static readonly TypeInfo s_internalCalculator = + typeof(BigInteger).GetTypeInfo() + .Assembly + .GetType("System.Numerics.BigIntegerCalculator") + .GetTypeInfo(); + + public static void RunWithFakeThreshold(string name, int value, Action action) + { + FieldInfo field = s_internalCalculator.GetDeclaredField(name); + int lastValue = (int)field.GetValue(null); + field.SetValue(null, value); + try + { + action(); + } + finally + { + field.SetValue(null, lastValue); + } + } } } diff --git a/src/System.Runtime.Numerics/tests/BigInteger/modpow.cs b/src/System.Runtime.Numerics/tests/BigInteger/modpow.cs index 4432e8a80a73..c04a1e5f1ff8 100644 --- a/src/System.Runtime.Numerics/tests/BigInteger/modpow.cs +++ b/src/System.Runtime.Numerics/tests/BigInteger/modpow.cs @@ -17,10 +17,6 @@ public static void ModPowValidSmallNumbers() BigInteger result; bool b = BigInteger.TryParse("22", out result); - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; - // ModPow Method - with small numbers - valid for (int i = 1; i <= 1; i++)//-2 { @@ -40,9 +36,9 @@ public static void ModPowValidSmallNumbers() [Fact] public static void ModPowNegative() { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; + byte[] tempByteArray1; + byte[] tempByteArray2; + byte[] tempByteArray3; // ModPow Method - with small numbers - invalid - zero modulus @@ -102,9 +98,9 @@ public static void ModPowNegative() [Fact] public static void ModPow3SmallInt() { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; + byte[] tempByteArray1; + byte[] tempByteArray2; + byte[] tempByteArray3; // ModPow Method - Three Small BigIntegers for (int i = 0; i < s_samples; i++) @@ -119,9 +115,9 @@ public static void ModPow3SmallInt() [Fact] public static void ModPow1Large2SmallInt() { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; + byte[] tempByteArray1; + byte[] tempByteArray2; + byte[] tempByteArray3; // ModPow Method - One large and two small BigIntegers for (int i = 0; i < s_samples; i++) @@ -143,12 +139,19 @@ public static void ModPow1Large2SmallInt() } } + [Fact] + public static void ModPow1Large2SmallInt_Threshold() + { + // Again, with lower threshold + BigIntTools.Utils.RunWithFakeThreshold("ReducerThreshold", 8, ModPow1Large2SmallInt); + } + [Fact] public static void ModPow2Large1SmallInt() { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; + byte[] tempByteArray1; + byte[] tempByteArray2; + byte[] tempByteArray3; // ModPow Method - Two large and one small BigIntegers for (int i = 0; i < s_samples; i++) @@ -160,12 +163,44 @@ public static void ModPow2Large1SmallInt() } } + [Fact] + public static void ModPow2Large1SmallInt_Threshold() + { + // Again, with lower threshold + BigIntTools.Utils.RunWithFakeThreshold("ReducerThreshold", 8, ModPow2Large1SmallInt); + } + + [Fact] + [OuterLoop] + public static void ModPow3LargeInt() + { + byte[] tempByteArray1; + byte[] tempByteArray2; + byte[] tempByteArray3; + + // ModPow Method - Three large BigIntegers + for (int i = 0; i < s_samples; i++) + { + tempByteArray1 = GetRandomByteArray(s_random); + tempByteArray2 = GetRandomPosByteArray(s_random); + tempByteArray3 = GetRandomByteArray(s_random); + VerifyModPowString(Print(tempByteArray3) + Print(tempByteArray2) + Print(tempByteArray1) + "tModPow"); + } + } + + [Fact] + [OuterLoop] + public static void ModPow3LargeInt_Threshold() + { + // Again, with lower threshold + BigIntTools.Utils.RunWithFakeThreshold("ReducerThreshold", 8, ModPow3LargeInt); + } + [Fact] public static void ModPow0Power() { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; + byte[] tempByteArray1; + byte[] tempByteArray2; // ModPow Method - zero power for (int i = 0; i < s_samples; i++) @@ -191,9 +226,8 @@ public static void ModPow0Power() [Fact] public static void ModPow0Base() { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; + byte[] tempByteArray1; + byte[] tempByteArray2; // ModPow Method - zero base for (int i = 0; i < s_samples; i++) @@ -219,9 +253,9 @@ public static void ModPow0Base() [Fact] public static void ModPowAxiom() { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; + byte[] tempByteArray1; + byte[] tempByteArray2; + byte[] tempByteArray3; // Axiom (x^y)%z = modpow(x,y,z) for (int i = 0; i < s_samples; i++) @@ -239,10 +273,6 @@ public static void ModPowAxiom() [Fact] public static void ModPowBoundary() { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; - byte[] tempByteArray3 = new byte[0]; - // Check interesting cases for boundary conditions // You'll either be shifting a 0 or 1 across the boundary // 32 bit boundary n2=0 diff --git a/src/System.Runtime.Numerics/tests/BigInteger/multiply.cs b/src/System.Runtime.Numerics/tests/BigInteger/multiply.cs index e04b34f9fb80..6e2cea50767d 100644 --- a/src/System.Runtime.Numerics/tests/BigInteger/multiply.cs +++ b/src/System.Runtime.Numerics/tests/BigInteger/multiply.cs @@ -34,6 +34,14 @@ public static void RunMultiply_TwoLargeBigIntegers() } } + [Fact] + public static void RunMultiply_TwoLargeBigIntegers_Threshold() + { + // Again, with lower threshold + BigIntTools.Utils.RunWithFakeThreshold("SquareThreshold", 8, RunMultiply_TwoLargeBigIntegers); + BigIntTools.Utils.RunWithFakeThreshold("MultiplyThreshold", 8, RunMultiply_TwoLargeBigIntegers); + } + [Fact] public static void RunMultiply_TwoSmallBigIntegers() {