Skip to content

Commit

Permalink
Merge pull request dotnet/corefx#2182 from axelheer/biginteger-perfor…
Browse files Browse the repository at this point in the history
…mance

Improve performance of BigInteger.Pow/ModPow

Commit migrated from dotnet/corefx@3279ca8
  • Loading branch information
stephentoub committed Jul 9, 2015
2 parents 852ea0a + 514f343 commit 15122a1
Show file tree
Hide file tree
Showing 11 changed files with 987 additions and 370 deletions.
Expand Up @@ -19,7 +19,10 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Numerics\BigIntegerCalculator.AddSub.cs" />
<Compile Include="System\Numerics\BigIntegerCalculator.BitsBuffer.cs" />
<Compile Include="System\Numerics\BigIntegerCalculator.DivRem.cs" />
<Compile Include="System\Numerics\BigIntegerCalculator.FastReducer.cs" />
<Compile Include="System\Numerics\BigIntegerCalculator.PowMod.cs" />
<Compile Include="System\Numerics\BigIntegerCalculator.SquMul.cs" />
<Compile Include="System\Numerics\BigInteger.cs" />
<Compile Include="System\Numerics\BigIntegerBuilder.cs" />
Expand Down
202 changes: 34 additions & 168 deletions src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
Expand Up @@ -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)
Expand All @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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; } }
Expand Down Expand Up @@ -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;)
Expand Down
Expand Up @@ -197,6 +197,7 @@ public unsafe static uint[] Subtract(uint[] left, uint[] right)
bits[i] = (uint)digit;
carry = digit >> 32;
}

Debug.Assert(carry == 0);
}

Expand Down

0 comments on commit 15122a1

Please sign in to comment.