/// <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); } }
/// <summary> /// Compares two BigInts /// </summary> /// <returns>1 if a > b, 0 if a == b, -1 if a is less than b</returns> public static short Compare(BigInt a, BigInt b) { a.EnsureInitialized(); b.EnsureInitialized(); if (a.IsZero() && b.IsZero()) { return(0); } if (a.Sign != b.Sign) { //a and b have different signs if (a.Sign == 1) { return(1); //every positive number is bigger than every negative number } else { return(-1); //and vice versa } } else if (a.NumDigits > b.NumDigits) { //a has more digits than b (a and b have same sign) return(a.Sign); // If both are positive then a > b. If both are negative then a < b } else if (a.NumDigits < b.NumDigits) { //a has fewer digits than b (a and b have same sign) return((short)-a.Sign); // If both are positive then a < b. If both are negative then a > b } else { short aChunk, bChunk; for (int i = a.NumChunks - 1; i >= 0; i--) { aChunk = a.GetChunk(i); bChunk = b.GetChunk(i); if (aChunk < bChunk) { return(-1); } else if (aChunk > bChunk) { return(1); } } //they must be equal return(0); } }
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(); } }