// This leaves the GCD in reg1 and trash in reg2. // This uses Lehmer's method, with test due to Jebelean / Belnkiy and Vidunas. // See Knuth, vol 2, page 345; Jebelean (1993) "Improving the Multiprecision Euclidean Algorithm"; // and Belenkiy & Vidunas (1998) "A Greatest Common Divisor Algorithm". private static void LehmerGcd(ref BigIntegerBuilder reg1, ref BigIntegerBuilder reg2) { // This value has no real significance. Occ----ionally we want to subtract // the two registers and keep the absolute value of the difference. To do // so we need to pass a ref sign to Sub. int signTmp = +1; for (;;) { int cuMax = reg1._iuLast + 1; int cuMin = reg2._iuLast + 1; if (cuMax < cuMin) { NumericsHelpers.Swap(ref reg1, ref reg2); NumericsHelpers.Swap(ref cuMax, ref cuMin); } if (cuMin == 1) { if (cuMax == 1) { reg1._uSmall = NumericsHelpers.GCD(reg1._uSmall, reg2._uSmall); } else if (reg2._uSmall != 0) { reg1.Set(NumericsHelpers.GCD(Mod(ref reg1, reg2._uSmall), reg2._uSmall)); } return; } if (cuMax == 2) { reg1.Set(NumericsHelpers.GCD(reg1.GetHigh2(2), reg2.GetHigh2(2))); return; } if (cuMin <= cuMax - 2) { // reg1 is much larger than reg2, so just mod. reg1.Mod(ref reg2); continue; } ulong uu1 = reg1.GetHigh2(cuMax); ulong uu2 = reg2.GetHigh2(cuMax); int cbit = NumericsHelpers.CbitHighZero(uu1 | uu2); if (cbit > 0) { uu1 = (uu1 << cbit) | (reg1._rgu[cuMax - 3] >> (kcbitUint - cbit)); // Note that [cuMax - 3] is correct, NOT [cuMin - 3]. uu2 = (uu2 << cbit) | (reg2._rgu[cuMax - 3] >> (kcbitUint - cbit)); } if (uu1 < uu2) { NumericsHelpers.Swap(ref uu1, ref uu2); NumericsHelpers.Swap(ref reg1, ref reg2); } // Make sure we don't overflow. if (uu1 == ulong.MaxValue || uu2 == ulong.MaxValue) { uu1 >>= 1; uu2 >>= 1; } if (uu1 == uu2) { // The high bits are the same, so we don't know which // is larger. No matter, just subtract one from the other // and keep the absolute value of the result. reg1.Sub(ref signTmp, ref reg2); continue; } if (NumericsHelpers.GetHi(uu2) == 0) { // reg1 is much larger than reg2, so just mod. reg1.Mod(ref reg2); continue; } // These are the coefficients to apply to reg1 and reg2 to get // the new values, using: a * reg1 - b * reg2 and -c * reg1 + d * reg2. uint a = 1, b = 0; uint c = 0, d = 1; for (;;) { uint uQuo = 1; ulong uuNew = uu1 - uu2; while (uuNew >= uu2 && uQuo < 32) { uuNew -= uu2; uQuo++; } if (uuNew >= uu2) { ulong uuQuo = uu1 / uu2; if (uuQuo > uint.MaxValue) { break; } uQuo = (uint)uuQuo; uuNew = uu1 - uQuo * uu2; } ulong uuAdNew = a + (ulong)uQuo * c; ulong uuBcNew = b + (ulong)uQuo * d; if (uuAdNew > int.MaxValue || uuBcNew > int.MaxValue) { break; } // Jebelean / Belenkiy-Vidunas conditions if (uuNew < uuBcNew || uuNew + uuAdNew > uu2 - c) { break; } a = (uint)uuAdNew; b = (uint)uuBcNew; uu1 = uuNew; if (uu1 <= b) { break; } uQuo = 1; uuNew = uu2 - uu1; while (uuNew >= uu1 && uQuo < 32) { uuNew -= uu1; uQuo++; } if (uuNew >= uu1) { ulong uuQuo = uu2 / uu1; if (uuQuo > uint.MaxValue) { break; } uQuo = (uint)uuQuo; uuNew = uu2 - uQuo * uu1; } uuAdNew = d + (ulong)uQuo * b; uuBcNew = c + (ulong)uQuo * a; if (uuAdNew > int.MaxValue || uuBcNew > int.MaxValue) { break; } // Jebelean / Belenkiy-Vidunas conditions if (uuNew < uuBcNew || uuNew + uuAdNew > uu1 - b) { break; } d = (uint)uuAdNew; c = (uint)uuBcNew; uu2 = uuNew; if (uu2 <= c) { break; } } if (b == 0) { if (uu1 / 2 >= uu2) { reg1.Mod(ref reg2); } else { reg1.Sub(ref signTmp, ref reg2); } } else { // Replace reg1 with a * reg1 - b * reg2. // Replace reg2 with -c * reg1 + d * reg2. // Do everything mod cuMin uint's. reg1.SetSizeKeep(cuMin, 0); reg2.SetSizeKeep(cuMin, 0); int nCarry1 = 0; int nCarry2 = 0; for (int iu = 0; iu < cuMin; iu++) { uint u1 = reg1._rgu[iu]; uint u2 = reg2._rgu[iu]; long nn1 = (long)u1 * a - (long)u2 * b + nCarry1; long nn2 = (long)u2 * d - (long)u1 * c + nCarry2; nCarry1 = (int)(nn1 >> kcbitUint); nCarry2 = (int)(nn2 >> kcbitUint); reg1._rgu[iu] = (uint)nn1; reg2._rgu[iu] = (uint)nn2; } reg1.Trim(); reg2.Trim(); } } }
private static void ModDivCore(ref BigIntegerBuilder regNum, ref BigIntegerBuilder regDen, bool fQuo, ref BigIntegerBuilder regQuo) { regQuo.Set(0); if (regNum._iuLast < regDen._iuLast) { return; } int cuDen = regDen._iuLast + 1; int cuDiff = regNum._iuLast - regDen._iuLast; // Determine whether the result will have cuDiff "digits" or cuDiff+1 "digits". int cuQuo = cuDiff; for (int iu = regNum._iuLast; ; iu--) { if (iu < cuDiff) { cuQuo++; break; } if (regDen._rgu[iu - cuDiff] != regNum._rgu[iu]) { if (regDen._rgu[iu - cuDiff] < regNum._rgu[iu]) { cuQuo++; } break; } } if (cuQuo == 0) { return; } if (fQuo) { regQuo.SetSizeLazy(cuQuo); } // Get the uint to use for the trial divisions. We normalize so the high bit is set. uint uDen = regDen._rgu[cuDen - 1]; uint uDenNext = regDen._rgu[cuDen - 2]; int cbitShiftLeft = NumericsHelpers.CbitHighZero(uDen); int cbitShiftRight = kcbitUint - cbitShiftLeft; if (cbitShiftLeft > 0) { uDen = (uDen << cbitShiftLeft) | (uDenNext >> cbitShiftRight); uDenNext <<= cbitShiftLeft; if (cuDen > 2) { uDenNext |= regDen._rgu[cuDen - 3] >> cbitShiftRight; } } // Allocate and initialize working space. regNum.EnsureWritable(); for (int iu = cuQuo; --iu >= 0;) { // Get the high (normalized) bits of regNum. uint uNumHi = (iu + cuDen <= regNum._iuLast) ? regNum._rgu[iu + cuDen] : 0; ulong uuNum = NumericsHelpers.MakeUlong(uNumHi, regNum._rgu[iu + cuDen - 1]); uint uNumNext = regNum._rgu[iu + cuDen - 2]; if (cbitShiftLeft > 0) { uuNum = (uuNum << cbitShiftLeft) | (uNumNext >> cbitShiftRight); uNumNext <<= cbitShiftLeft; if (iu + cuDen >= 3) { uNumNext |= regNum._rgu[iu + cuDen - 3] >> cbitShiftRight; } } // Divide to get the quotient digit. ulong uuQuo = uuNum / uDen; ulong uuRem = (uint)(uuNum % uDen); if (uuQuo > uint.MaxValue) { uuRem += uDen * (uuQuo - uint.MaxValue); uuQuo = uint.MaxValue; } while (uuRem <= uint.MaxValue && uuQuo * uDenNext > NumericsHelpers.MakeUlong((uint)uuRem, uNumNext)) { uuQuo--; uuRem += uDen; } // Multiply and subtract. Note that uuQuo may be 1 too large. If we have a borrow // at the end, we'll add the denominator back on and decrement uuQuo. if (uuQuo > 0) { ulong uuBorrow = 0; for (int iu2 = 0; iu2 < cuDen; iu2++) { uuBorrow += regDen._rgu[iu2] * uuQuo; uint uSub = (uint)uuBorrow; uuBorrow >>= kcbitUint; if (regNum._rgu[iu + iu2] < uSub) { uuBorrow++; } regNum._rgu[iu + iu2] -= uSub; } if (uNumHi < uuBorrow) { // Add, tracking carry. uint uCarry = 0; for (int iu2 = 0; iu2 < cuDen; iu2++) { uCarry = AddCarry(ref regNum._rgu[iu + iu2], regDen._rgu[iu2], uCarry); } uuQuo--; } regNum._iuLast = iu + cuDen - 1; } if (fQuo) { if (cuQuo == 1) { regQuo._uSmall = (uint)uuQuo; } else { regQuo._rgu[iu] = (uint)uuQuo; } } } regNum._iuLast = cuDen - 1; regNum.Trim(); }
static String FormatBigInteger(BigInteger value, String format, NumberFormatInfo info) { int digits = 0; char fmt = ParseFormatSpecifier(format, out digits); if (fmt == 'x' || fmt == 'X') { return(FormatBigIntegerToHexString(value, fmt, digits, info)); } bool decimalFmt = (fmt == 'g' || fmt == 'G' || fmt == 'd' || fmt == 'D' || fmt == 'r' || fmt == 'R'); if (value._bits == null) { if (fmt == 'g' || fmt == 'G' || fmt == 'r' || fmt == 'R') { if (digits > 0) { format = String.Format(CultureInfo.InvariantCulture, "D{0}", digits.ToString(CultureInfo.InvariantCulture)); } else { format = "D"; } } return(value._sign.ToString(format, info)); } // First convert to base 10^9. const uint kuBase = 1000000000; // 10^9 const int kcchBase = 9; int cuSrc = BigInteger.Length(value._bits); int cuMax; try { cuMax = checked (cuSrc * 10 / 9 + 2); } catch (OverflowException e) { throw new FormatException("SR.Format_TooLarge", e); } uint[] rguDst = new uint[cuMax]; int cuDst = 0; for (int iuSrc = cuSrc; --iuSrc >= 0;) { uint uCarry = value._bits[iuSrc]; for (int iuDst = 0; iuDst < cuDst; iuDst++) { ulong uuRes = NumericsHelpers.MakeUlong(rguDst[iuDst], uCarry); rguDst[iuDst] = (uint)(uuRes % kuBase); uCarry = (uint)(uuRes / kuBase); } if (uCarry != 0) { rguDst[cuDst++] = uCarry % kuBase; uCarry /= kuBase; if (uCarry != 0) { rguDst[cuDst++] = uCarry; } } } int cchMax; try { // Each uint contributes at most 9 digits to the decimal representation. cchMax = checked (cuDst * kcchBase); } catch (OverflowException e) { throw new FormatException("SR.Format_TooLarge", e); } if (decimalFmt) { if (digits > 0 && digits > cchMax) { cchMax = digits; } if (value._sign < 0) { try { // Leave an extra slot for a minus sign. cchMax = checked (cchMax + info.NegativeSign.Length); } catch (OverflowException e) { throw new FormatException("SR.Format_TooLarge", e); } } } int rgchBufSize; try { // We'll pass the rgch buffer to native code, which is going to treat it like a string of digits, so it needs // to be null terminated. Let's ensure that we can allocate a buffer of that size. rgchBufSize = checked (cchMax + 1); } catch (OverflowException e) { throw new FormatException("SR.Format_TooLarge", e); } char[] rgch = new char[rgchBufSize]; int ichDst = cchMax; for (int iuDst = 0; iuDst < cuDst - 1; iuDst++) { uint uDig = rguDst[iuDst]; for (int cch = kcchBase; --cch >= 0;) { rgch[--ichDst] = (char)('0' + uDig % 10); uDig /= 10; } } for (uint uDig = rguDst[cuDst - 1]; uDig != 0;) { rgch[--ichDst] = (char)('0' + uDig % 10); uDig /= 10; } #if !SILVERLIGHT || FEATURE_NETCORE if (!decimalFmt) { // // Go to the VM for GlobLoc aware formatting // Byte *numberBufferBytes = stackalloc Byte[NumberPort.NumberBuffer.NumberBufferBytes]; NumberPort.NumberBuffer number = new NumberPort.NumberBuffer(numberBufferBytes); // sign = true for negative and false for 0 and positive values number.sign = (value._sign < 0); // the cut-off point to switch (G)eneral from (F)ixed-point to (E)xponential form number.precision = 29; number.digits[0] = '\0'; number.scale = cchMax - ichDst; int maxDigits = Math.Min(ichDst + 50, cchMax); for (int i = ichDst; i < maxDigits; i++) { number.digits[i - ichDst] = rgch[i]; } fixed(char *pinnedExtraDigits = rgch) { throw new NotImplementedException($"Could not implement NumberPort.FormatNumberBuffer. Therefore, some {nameof(BigInteger)} functionality is unimplemented."); } } #endif //!SILVERLIGHT ||FEATURE_NETCORE // Format Round-trip decimal // This format is supported for integral types only. The number is converted to a string of // decimal digits (0-9), prefixed by a minus sign if the number is negative. The precision // specifier indicates the minimum number of digits desired in the resulting string. If required, // the number is padded with zeros to its left to produce the number of digits given by the // precision specifier. int numDigitsPrinted = cchMax - ichDst; while (digits > 0 && digits > numDigitsPrinted) { // pad leading zeros rgch[--ichDst] = '0'; digits--; } if (value._sign < 0) { String negativeSign = info.NegativeSign; for (int i = info.NegativeSign.Length - 1; i > -1; i--) { rgch[--ichDst] = info.NegativeSign[i]; } } return(new String(rgch, ichDst, cchMax - ichDst)); }