private static BigInt DivideAndConquerMultiply(BigInt a, BigInt b) { //faster divide-and-conquer multiplication, called by operator* when a and b are //large. Do not call directly. int numChunks = MaxNumChunks(a, b); if (MinNumChunks(a, b) < D_AND_C_THRESHOLD) { return(a * b); } if ((numChunks & 1) == 1) // if numChunks is odd, add 1 { numChunks++; } int N = numChunks / 2; int Ndigits = N + N + N; // N chunks = 3N digits //set a0,a1,b0,b1 such that a = (1000^N)*A1 + A0, b = (1000^N)*B1 + B0 //then a*b = (1000^(2N) + 1000^N)A1B1 + 1000^N(A1-A0)(B0-B1) + (1000^N + 1)A0B0 int capacity = StorageSizeForDigits(a.NumDigits + b.NumDigits); BigInt a0 = new BigInt(capacity, 0); BigInt a1 = new BigInt(capacity, 0); BigInt b0 = new BigInt(capacity, 0); BigInt b1 = new BigInt(capacity, 0); for (int i = 0; i < N; i++) { a0.AppendChunk((ushort)a.GetChunkAbsolute(i)); a1.AppendChunk((ushort)a.GetChunkAbsolute(i + N)); b0.AppendChunk((ushort)b.GetChunkAbsolute(i)); b1.AppendChunk((ushort)b.GetChunkAbsolute(i + N)); } a0.AfterUpdate(); //TODO: get rid of these? and call result.AfterUpdate? a1.AfterUpdate(); b0.AfterUpdate(); b1.AfterUpdate(); BigInt part1 = DivideAndConquerMultiply(a1, b1); BigInt part2 = DivideAndConquerMultiply(a1 - a0, b0 - b1); BigInt part3 = DivideAndConquerMultiply(a0, b0); BigInt result = (part1 << 2 * Ndigits) + (part1 << Ndigits) + (part2 << Ndigits) + (part3 << Ndigits) + part3; result._sign = (short)(a.Sign * b.Sign); //-ve * -ve == +ve, etc return(result); }
/// <summary> /// Compares the absolute values of two BigInts /// </summary> /// <returns>1 if |a| > |b|, 0 if |a| == |b|, -1 if |a| is less than |b|</returns> private static short CompareMagnitudes(BigInt a, BigInt b) { if (a.IsZero() && b.IsZero()) { return(0); } if (a.NumDigits > b.NumDigits) { //a has more digits than b so it must be bigger return((short)1); } else if (a.NumDigits < b.NumDigits) { //a has fewer digits than b so it must be smaller return((short)-1); } else { short aChunk, bChunk; for (int i = a.NumChunks - 1; i >= 0; i--) { aChunk = a.GetChunkAbsolute(i); bChunk = b.GetChunkAbsolute(i); if (aChunk < bChunk) { return(-1); } else if (aChunk > bChunk) { return(1); } } //they must be equal return(0); } }
public static BigInt ModPower(BigInt a, BigInt b, BigInt c) { // returns (a ^ b) mod c // eg 28 ^ 23 mod 55 = 7 // There's a trick to this. a ^ b is usually way too big to handle, so we break it // into pieces, using the fact that xy mod c = ( x mod c )( y mod c ) mod c. // Please see www.carljohansen.co.uk/rsa for a full explanation. a.EnsureInitialized(); b.EnsureInitialized(); c.EnsureInitialized(); int numBdigits = b.NumDigits; BigInt prevResult, finalProduct = 1; int i; BigInt[] arrResidues = new BigInt[numBdigits + 1]; BigInt accumulation2, accumulation4, accumulation8; arrResidues[0] = a; prevResult = arrResidues[0]; for (i = 1; i < numBdigits; i++) { //have to calculate (prevResult^10) mod c. This is (prevResult^2^2^2) mod c * (prevResult^2) mod c accumulation2 = (prevResult * prevResult) % c; accumulation4 = (accumulation2 * accumulation2) % c; accumulation8 = (accumulation4 * accumulation4) % c; arrResidues[i] = (accumulation2 * accumulation8) % c; prevResult = arrResidues[i]; } for (i = numBdigits - 1; i >= 0; i--) { int currChunk = b.GetChunkAbsolute(i / 3); int currDigit; if (i % 3 == 0) { currDigit = currChunk % 10; } else if (i % 3 == 1) { currDigit = (currChunk % 100) / 10; } else { currDigit = currChunk / 100; } for (int j = 0; j < currDigit; j++) { finalProduct *= arrResidues[i]; finalProduct %= c; } } return(finalProduct); }
private static void DivAndMod(DivModOperationType operationType, BigInt a, BigInt b, out BigInt remainder, out BigInt result) // sets <result> to the result of dividing a by b, and sets <remainder> to the remainder when // a is divided by b { if (b.IsZero()) { throw new DivideByZeroException(); } if (CompareMagnitudes(a, b) < 0) // if a is less than b then it's a no-brainer { if (operationType != DivModOperationType.DivideOnly) { remainder = a; } else { remainder = 0; } result = 0; return; } if (a.NumChunks <= 3) { //a, b small - just use machine arithmetic remainder = 0; int intA = Convert.ToInt32(a.ToString()); int intB = Convert.ToInt32(b.ToString()); if (operationType != DivModOperationType.DivideOnly) { remainder = intA % intB; } if (operationType != DivModOperationType.ModulusOnly) { result = intA / intB; } else { result = 0; } return; } result = new BigInt(a.Capacity, 0); BigInt removal, resultStore = new BigInt(a.Capacity, 0); short resultSign = (short)(a.Sign * b.Sign); short bSign = b.Sign; ushort trialDiv; short comparison; int numBdigits = b.NumDigits; remainder = a; remainder._sign = 1; b._sign = 1; // work with positive numbers int bTrial = 0; if (b.NumChunks == 1) { bTrial = b.GetChunkAbsolute(b.NumChunks - 1); } else { bTrial = b.GetChunkAbsolute(b.NumChunks - 1) * 1000 + b.GetChunkAbsolute(b.NumChunks - 2); } int bTrialDigits = (int)Math.Floor(Math.Log10(bTrial)) + 1; int thisShift = 0, lastShift = -1; while (remainder >= b) { int lastRemainderDigits = remainder.NumDigits; int remTrial; short remMostSigChunk = remainder.GetChunkAbsolute(remainder._numChunks - 1); //remTrial is formed by taking the most significant 1, 2 or 3 chunks of remainder; // take the minimum number of chunks needed to make remTrial bigger than bTrial. if (remMostSigChunk >= bTrial) { remTrial = remMostSigChunk; } else { remTrial = remMostSigChunk * 1000 + remainder.GetChunkAbsolute(remainder.NumChunks - 2); if (remTrial < bTrial) { remTrial = (remTrial * 1000) + remainder.GetChunkAbsolute(remainder.NumChunks - 3); } //now remTrial should be >= bTrial. remTrial == bTrial is the nasty special case that produces trialDiv == 1. } trialDiv = (ushort)(remTrial / bTrial); // this is our guess of the next quotient chunk removal = b.ShortMultiply(trialDiv); int remTrialDigits = (int)Math.Floor(Math.Log10(remTrial)) + 1; thisShift = remainder.NumDigits - (remTrialDigits - bTrialDigits) - numBdigits; removal = removal << thisShift; // normally, the required shift drops by three (the number of digits in a chunk) with // each loop iteration. But if the result contains a 000 chunk in the middle, we // will notice a drop in shift of more than 3. if (operationType != DivModOperationType.ModulusOnly) { for (int i = 0; i < lastShift - thisShift - 3; i += 3) { resultStore.AppendChunk(0); } } // our guess of the next quotient chunk might be too high (but not by more than two); // adjust it down if necessary BigInt bShifted = 0; bool bShiftedSet = false; do { comparison = Compare(removal, remainder); if (comparison > 0) { trialDiv--; if (trialDiv == 0) { //This is the nasty special case. I think that the first three cases can // never happen. Effectively what happens is that have guessed 1000 (through // trialDiv = 1 and thisShift >= 3) and found that it is one high so we // reduce it to 999 (through trialDiv = 999 and thisShift -= 3). switch (thisShift) { case 0: //This should never happen break; case 1: trialDiv = 9; thisShift = 0; break; case 2: trialDiv = 99; thisShift = 0; break; default: trialDiv = 999; thisShift -= 3; break; } } if (!bShiftedSet) { bShiftedSet = true; bShifted = b << thisShift; } removal -= bShifted; } } while (comparison > 0); lastShift = thisShift; remainder -= removal; if (operationType != DivModOperationType.ModulusOnly) { resultStore.AppendChunk(trialDiv); } } b._sign = bSign; // put back b's sign if (operationType != DivModOperationType.DivideOnly) { remainder._sign = a.Sign; // sign of mod = sign of a remainder.AfterUpdate(); } if (operationType != DivModOperationType.ModulusOnly) { for (int i = 3; i <= thisShift; i += 3) { resultStore.AppendChunk(0); } //resultStore now holds the quotient chunks in reverse order. Flip them and return. for (int i = resultStore.NumChunks - 1; i >= 0; i--) { result.AppendChunk((ushort)resultStore.GetChunkAbsolute(i)); } result._sign = resultSign; result.AfterUpdate(); } }