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(); } }
public static BigInt operator ^(BigInt a, uint b) { a.EnsureInitialized(); return(BigInt.Power(a, b)); }
public static void DivAndMod(BigInt a, BigInt b, out BigInt remainder, out BigInt result) { a.EnsureInitialized(); b.EnsureInitialized(); DivAndMod(DivModOperationType.DivAndMod, a, b, out remainder, out result); }
public static BigInt operator +(BigInt a, BigInt b) { a.EnsureInitialized(); b.EnsureInitialized(); bool isSubtraction = false; BigInt result = new BigInt(Math.Max(a.Capacity, b.Capacity), 0); if (a.Sign != b.Sign) { bool doSignSwapAddition = false; if (a.Sign == -1) { doSignSwapAddition = (BigInt.CompareMagnitudes(a, b) == 1); } else //b.Sign == -1 { doSignSwapAddition = (BigInt.CompareMagnitudes(a, b) == -1); } if (doSignSwapAddition) { // our standard addition algorithm doesn't work when a < 0 and b > 0 and |a| > |b| (or vice versa) // becauase we don't look ahead to see if we can borrow, so instead calculate -((-a) + (-b)) a._sign = (short)-a.Sign; b._sign = (short)-b.Sign; result = -(a + b); a._sign = (short)-a.Sign; // put signs back b._sign = (short)-b.Sign; // the way they were return(result); } else { isSubtraction = true; } } int currChunkSum = 0, carry = 0, currChunkToAppend; int numChunksToAdd = MaxNumChunks(a, b); for (int i = 0; i < numChunksToAdd; i++) { short borrow = 0, aChunk = a.GetChunk(i), bChunk = b.GetChunk(i); if (isSubtraction && ((a.Sign == 1 && (aChunk + carry < Math.Abs(bChunk))) || (b.Sign == 1 && (bChunk + carry < Math.Abs(aChunk))))) { //we're adding +ve and -ve, and the chunk in the positive number (combined with carry) // is smaller than the chunk in the negative number, so have to borrow borrow = 1000; } currChunkSum = aChunk + bChunk + borrow + carry; if (borrow > 0) { carry = -1; currChunkToAppend = currChunkSum; } else if (currChunkSum >= 1000) { carry = 1; currChunkToAppend = currChunkSum - 1000; } else if (currChunkSum <= -1000) { carry = -1; currChunkToAppend = (-currChunkSum) - 1000; } else { carry = 0; currChunkToAppend = Math.Abs(currChunkSum); } result.AppendChunk((ushort)currChunkToAppend); } if (carry != 0) { result.AppendChunk((ushort)Math.Abs(carry)); } if (a._sign == b._sign) { result._sign = a._sign; } else { result._sign = (currChunkSum < 0) ? (short)-1 : (short)1; } result.AfterUpdate(); return(result); }
private static BigInt calculate(IEnumerable <BigInt> ts) { Dictionary <BigInt, int> d = new Dictionary <BigInt, int>(); foreach (var item in ts) { d[item] = 0; } var sortedList = d.Keys.ToList(); sortedList.Sort((x, y) => { if (x > y) { return(1); } else if (x < y) { return(-1); } else { return(0); } }); BigInt minDiff = sortedList[sortedList.Count - 1]; int minIdx = -1; var diffList = new List <BigInt>(); for (int i = 0; i < sortedList.Count - 1; i++) { var less = sortedList[i]; var more = sortedList[i + 1]; var diff = (more - less); if (minDiff > diff) { minDiff = diff; } diffList.Add(diff); } var gd = minDiff; for (int i = 0; i < diffList.Count; i++) { gd = getGreatestDivisor(gd, diffList[i]); if (gd == 1) { break; } } BigInt remainder = sortedList[0] % gd; if (remainder == 0) { return(0); } else { return(gd - remainder); } }