/// <summary> /// Bitwise Xor /// </summary> /// <param name="n1"></param> /// <param name="n2"></param> /// <returns></returns> public static BigInt Xor(BigInt n1, BigInt n2) { if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); BigInt res = new BigInt(n1); res.And(n2); return res; }
/// <summary> /// Floating-point helper function. /// Squares the number and keeps the high bits of the calculation. /// Takes a temporary BigInt as a working set. /// </summary> public void SquareHiFast(BigInt scratch) { int Length = digitArray.Length; uint[] tempDigits = scratch.digitArray; uint[] workingSet2 = scratch.workingSet; //Temp storage for source (both working sets are used by the calculation) for (int i = 0; i < Length; i++) { tempDigits[i] = digitArray[i]; digitArray[i] = 0; } for (int i = 0; i < Length; i++) { //Clear the working set for (int j = i; j < Length; j++) { workingSet[j] = 0; workingSet2[j] = 0; } if (i - 1 >= 0) workingSet[i - 1] = 0; ulong digit = tempDigits[i]; if (digit == 0) continue; for (int j = 0; j < Length; j++) { if (j + i + 1 < Length) continue; //Multiply n1 by each of the integer digits of n2. ulong temp = (ulong)tempDigits[j] * digit; //n1.workingSet stores the low bits of each piecewise multiplication if (j + i >= Length) { workingSet[j + i - Length] = (uint)(temp & 0xffffffff); } //n2.workingSet stores the high bits of each multiplication workingSet2[j + i + 1 - Length] = (uint)(temp >> 32); } AddInternalBits(workingSet); AddInternalBits(workingSet2); } sign = false; }
/// <summary> /// Calculates 'this' mod n2 (using the schoolbook division algorithm as above) /// </summary> /// <param name="n2"></param> public void Mod(BigInt n2) { if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); int OldLength = digitArray.Length; //First, we need to prepare the operands for division using Div_32, which requires //That the most significant digit of n2 be set. To do this, we need to shift n2 (and therefore n1) up. //This operation can potentially increase the precision of the operands. int shift = MakeSafeDiv(this, n2); BigInt Q = new BigInt(this.pres); BigInt R = new BigInt(this.pres); Q.digitArray = new UInt32[this.digitArray.Length]; R.digitArray = new UInt32[this.digitArray.Length]; Div_32(this, n2, Q, R); //Restore n2 to its pre-shift value n2.RSH(shift); R.RSH(shift); R.sign = (sign != n2.sign); AssignInt(R); //Reset the lengths of the operands SetNumDigits(OldLength); n2.SetNumDigits(OldLength); }
/// <summary> /// Makes sure the numbers have matching precisions /// </summary> /// <param name="n2">the number to match to this</param> private void MakeSafe(ref BigInt n2) { n2 = new BigInt(n2, pres); n2.SetNumDigits(digitArray.Length); }
/// <summary> /// Subtraction and assignment - without intermediate memory allocation. /// </summary> /// <param name="n2"></param> public uint Sub(BigInt n2) { if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); if (sign != n2.sign) { return AddInternalBits(n2.digitArray); } else { bool lessThan = LtInt(this, n2); if (lessThan) { int Length = digitArray.Length; for (int i = 0; i < Length; i++) { workingSet[i] = digitArray[i]; digitArray[i] = n2.digitArray[i]; } sign = !sign; return SubInternalBits(workingSet); } else { return SubInternalBits(n2.digitArray); } } }
private string BuildString(BigInt n, int m) { if (m == 0) return n.ToString(10); BigInt remainder; BigInt quotient = BigInt.DivRem(n, powersOfTen[m], out remainder); return BuildString(quotient, m - 1) + BuildString(remainder, m - 1); }
/// <summary> /// Sign-insensitive greater than comparison. /// unsafe if n1 and n2 disagree in precision /// </summary> /// <param name="n1"></param> /// <param name="n2"></param> /// <returns></returns> private static bool GtInt(BigInt n1, BigInt n2) { //MakeSafe(ref n1, ref n2); for (int i = n1.digitArray.Length - 1; i >= 0; i--) { if (n1.digitArray[i] > n2.digitArray[i]) return true; if (n1.digitArray[i] < n2.digitArray[i]) return false; } //equal return false; }
/// <summary> /// Gets the most significant bit /// </summary> /// <param name="value">the input to search for the MSB in</param> /// <returns>-1 if the input was zero, the position of the MSB otherwise</returns> public static int GetMSB(BigInt value) { int digit = value.GetMSD(); int bit = GetMSB(value.digitArray[digit]); return (digit << 5) + bit; }
/// <summary> /// Shifts and optionally precision-extends the arguments to prepare for Div_32 /// </summary> /// <param name="n1"></param> /// <param name="n2"></param> private static int MakeSafeDiv(BigInt n1, BigInt n2) { int shift = n2.GetDivBitshift(); int n1MSD = n1.GetMSD(); uint temp = n1.digitArray[n1MSD]; if (n1MSD == n1.digitArray.Length - 1 && ((temp << shift) >> shift) != n1.digitArray[n1MSD]) { //Precision-extend n1 and n2 if necessary int digits = n1.digitArray.Length; n1.SetNumDigits(digits + 1); n2.SetNumDigits(digits + 1); } //Logical left-shift n1 and n2 n1.LSH(shift); n2.LSH(shift); return shift; }
/// <summary> /// Used for casting between BigFloats of different precisions - this assumes /// that the number is a normalised mantissa. /// </summary> /// <param name="n2"></param> /// <returns>true if a round-up caused the high bits to become zero</returns> public bool AssignHigh(BigInt n2) { int length = digitArray.Length; int length2 = n2.digitArray.Length; int minWords = (length < length2) ? length : length2; bool bRet = false; for (int i = 1; i <= minWords; i++) { digitArray[length - i] = n2.digitArray[length2 - i]; } if (length2 > length && n2.digitArray[length2 - (length + 1)] >= 0x80000000) { bRet = IncrementInt(); //Because we are assuming normalisation, we set the top bit (it will already be set if //bRet is false. digitArray[length - 1] = digitArray[length - 1] | 0x80000000; } sign = n2.sign; return bRet; }
/// <summary> /// Constructor for copying length and precision /// </summary> /// <param name="inputToCopy">The BigInt to copy</param> /// <param name="precision">The precision of the new BigInt</param> /// <param name="bCopyLengthOnly">decides whether to copy the actual input, or just its digit length</param> /// <example><code>//Create an integer /// BigInt four = new BigInt(4, new PrecisionSpec(128, PrecisionSpec.BaseType.BIN)); /// /// //Pad four to double its usual number of digits (this does not affect the precision) /// four.Pad(); /// /// //Create a new, empty integer with matching precision, also padded to twice the usual length /// BigInt newCopy = new BigInt(four, four.Precision, true);</code></example> public BigInt(BigInt inputToCopy, PrecisionSpec precision, bool bCopyLengthOnly) { digitArray = new uint[inputToCopy.digitArray.Length]; workingSet = new uint[inputToCopy.digitArray.Length]; if (!bCopyLengthOnly) Array.Copy(inputToCopy.digitArray, digitArray, digitArray.Length); sign = inputToCopy.sign; pres = inputToCopy.pres; }
/// <summary> /// Used to subtract n1 (true subtraction of digit arrays) - n1 must be less than or equal to this number /// </summary> /// <param name="n1"></param> private uint SubInternal(BigInt n1) { return SubInternalBits(n1.digitArray); }
/// <summary> /// Used to add with matching signs (true addition of the digit arrays) /// This is internal because it will fail spectacularly if n1 is negative. /// </summary> /// <param name="n1"></param> private uint AddInternal(BigInt n1) { return AddInternalBits(n1.digitArray); }
/// <summary> /// The right-shift operator /// </summary> public static BigInt operator >>(BigInt n1, int n2) { BigInt res = new BigInt(n1); res.RSH(n2); return res; }
/// <summary> /// Constructs a bigint from the input, matching the new precision provided /// </summary> public BigInt(BigInt input, PrecisionSpec precision) { //Casts the input to the new precision. Init(precision); int Min = (input.digitArray.Length < digitArray.Length) ? input.digitArray.Length : digitArray.Length; for (int i = 0; i < Min; i++) { digitArray[i] = input.digitArray[i]; } sign = input.sign; }
/// <summary> /// Schoolbook division helper function. /// </summary> /// <param name="n1"></param> /// <param name="n2"></param> /// <param name="Q">Quotient output value</param> /// <param name="R">Remainder output value</param> private static void Div_31(BigInt n1, BigInt n2, BigInt Q, BigInt R) { int digitsN1 = n1.GetMSD() + 1; int digitsN2 = n2.GetMSD() + 1; if ((digitsN1 > digitsN2)) { BigInt n1New = new BigInt(n2); n1New.DigitShiftSelfLeft(1); //If n1 >= n2 * 2^32 if (!LtInt(n1, n1New)) { n1New.sign = n1.sign; SubFast(n1New, n1, n1New); Div_32(n1New, n2, Q, R); //Q = (A - B*2^32)/B + 2^32 Q.Add2Pow32Self(); return; } } UInt32 q = 0; if (digitsN1 >= 2) { UInt64 q64 = ((((UInt64)n1.digitArray[digitsN1 - 1]) << 32) + n1.digitArray[digitsN1 - 2]) / (UInt64)n2.digitArray[digitsN2 - 1]; if (q64 > 0xfffffffful) { q = 0xffffffff; } else { q = (UInt32)q64; } } BigInt temp = Mul(n2, q); if (GtInt(temp, n1)) { temp.SubInternalBits(n2.digitArray); q--; if (GtInt(temp, n1)) { temp.SubInternalBits(n2.digitArray); q--; } } Q.Zero(); Q.digitArray[0] = q; R.Assign(n1); R.SubInternalBits(temp.digitArray); }
//********************* ToString members ********************** public String ToQuickString() { powersOfTen = new List<BigInt>(); powersOfTen.Add(new BigInt((int)1, new PrecisionSpec(1, PrecisionSpec.BaseType.DEC))); for (BigInt i = new BigInt((int)10, new PrecisionSpec(1, PrecisionSpec.BaseType.DEC)); i.LessThan(this); i *= i) { powersOfTen.Add(i); } return BuildString(this, powersOfTen.Count - 1).TrimStart('0'); }
/// <summary> /// Schoolbook division algorithm /// </summary> /// <param name="n1"></param> /// <param name="n2"></param> /// <param name="Q"></param> /// <param name="R"></param> private static void Div_32(BigInt n1, BigInt n2, BigInt Q, BigInt R) { int digitsN1 = n1.GetMSD() + 1; int digitsN2 = n2.GetMSD() + 1; //n2 is bigger than n1 if (digitsN1 < digitsN2) { R.AssignInt(n1); Q.Zero(); return; } if (digitsN1 == digitsN2) { //n2 is bigger than n1 if (LtInt(n1, n2)) { R.AssignInt(n1); Q.Zero(); return; } //n2 >= n1, but less the 2x n1 (initial conditions make this certain) Q.Zero(); Q.digitArray[0] = 1; R.Assign(n1); R.SubInternalBits(n2.digitArray); return; } int digits = digitsN1 - (digitsN2 + 1); //Algorithm Div_31 can be used to get the answer in O(n) time. if (digits == 0) { Div_31(n1, n2, Q, R); return; } BigInt n1New = DigitShiftRight(n1, digits); BigInt s = DigitTruncate(n1, digits); BigInt Q2 = new BigInt(n1, n1.pres, true); BigInt R2 = new BigInt(n1, n1.pres, true); Div_31(n1New, n2, Q2, R2); R2.DigitShiftSelfLeft(digits); R2.Add(s); Div_32(R2, n2, Q, R); Q2.DigitShiftSelfLeft(digits); Q.Add(Q2); }
/// <summary> /// Converts this to a string, in the specified base /// </summary> /// <param name="numberBase">the base to use (min 2, max 16)</param> /// <returns>a string representation of the number</returns> public string ToString(int numberBase) { char[] digitChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; string output = ""; BigInt clone = new BigInt(this); clone.sign = false; int numDigits = 0; while (!clone.IsZero()) { BigInt div, mod; DivMod(clone, (uint)numberBase, out div, out mod); int iMod = (int)mod; output = String.Concat(digitChars[(int)mod], output); numDigits++; clone = div; } if (output.Length == 0) output = "0"; if (sign) output = String.Concat("-", output); return output; }
/// <summary> /// Assigns n2 to 'this' /// </summary> /// <param name="n2"></param> public void Assign(BigInt n2) { if (digitArray.Length != n2.digitArray.Length) MakeSafe(ref n2); sign = n2.sign; AssignInt(n2); }
/// <summary> /// Makes sure the numbers have matching precisions /// </summary> /// <param name="n1"></param> /// <param name="n2"></param> private static void MakeSafe(ref BigInt n1, ref BigInt n2) { if (n1.digitArray.Length == n2.digitArray.Length) { return; } else if (n1.digitArray.Length > n2.digitArray.Length) { n2 = new BigInt(n2, n1.pres); } else { n1 = new BigInt(n1, n2.pres); } }
/// <summary> /// Assign n2 to 'this', safe only if precision-matched /// </summary> /// <param name="n2"></param> /// <returns></returns> private void AssignInt(BigInt n2) { int Length = digitArray.Length; for (int i = 0; i < Length; i++) { digitArray[i] = n2.digitArray[i]; } }
//*************** Utility Functions ************** /// <summary> /// Casts a BigInt to the new precision provided. /// Note: This will return the input if the precision already matches. /// </summary> /// <param name="input"></param> /// <param name="precision"></param> /// <returns></returns> public static BigInt CastToPrecision(BigInt input, PrecisionSpec precision) { if (input.pres == precision) return input; return new BigInt(input, precision); }
private static BigInt DigitShiftRight(BigInt n1, int digits) { BigInt res = new BigInt(n1); int Length = res.digitArray.Length; for (int i = 0; i < Length - digits; i++) { res.digitArray[i] = res.digitArray[i + digits]; } for (int i = Length - digits; i < Length; i++) { res.digitArray[i] = 0; } return res; }
/// <summary> /// Used for floating-point multiplication /// Stores the high bits of the multiplication only (the carry bit from the /// lower bits is missing, so the true answer might be 1 greater). /// </summary> /// <param name="n2"></param> public void MulHi(BigInt n2) { if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); int Length = n2.digitArray.Length; //Inner loop zero-mul avoidance int maxDigit = 0; for (int i = Length - 1; i >= 0; i--) { if (digitArray[i] != 0) { maxDigit = i + 1; break; } } //Result is zero, 'this' is unchanged if (maxDigit == 0) return; //Temp storage for source (both working sets are used by the calculation) uint[] thisTemp = new uint[Length]; for (int i = 0; i < Length; i++) { thisTemp[i] = digitArray[i]; digitArray[i] = 0; } for (int i = 0; i < Length; i++) { //Clear the working set for (int j = 0; j < Length; j++) { workingSet[j] = 0; n2.workingSet[j] = 0; } n2.workingSet[i] = 0; ulong digit = n2.digitArray[i]; if (digit == 0) continue; //Only the high bits if (maxDigit + i < Length - 1) continue; for (int j = 0; j < maxDigit; j++) { if (j + i + 1 < Length) continue; //Multiply n1 by each of the integer digits of n2. ulong temp = (ulong)thisTemp[j] * digit; //n1.workingSet stores the low bits of each piecewise multiplication if (j + i >= Length) { workingSet[j + i - Length] = (uint)(temp & 0xffffffff); } //n2.workingSet stores the high bits of each multiplication n2.workingSet[j + i + 1 - Length] = (uint)(temp >> 32); } AddInternalBits(workingSet); AddInternalBits(n2.workingSet); } sign = (sign != n2.sign); }
/// <summary> /// Constructs a bigint from the input. /// </summary> /// <param name="input"></param> public BigInt(BigInt input) { digitArray = new uint[input.digitArray.Length]; workingSet = new uint[input.digitArray.Length]; Array.Copy(input.digitArray, digitArray, digitArray.Length); sign = input.sign; pres = input.pres; }
/// <summary> /// This function is used for floating-point division. /// </summary> /// <param name="n2"></param> //Given two numbers: // In floating point 1 <= a, b < 2, meaning that both numbers have their top bits set. // To calculate a / b, maintaining precision, we: // 1. Double the number of digits available to both numbers. // 2. set a = a * 2^d (where d is the number of digits) // 3. calculate the quotient a <- q: 2^(d-1) <= q < 2^(d+1) // 4. if a >= 2^d, s = 1, else s = 0 // 6. shift a down by s, and undo the precision extension // 7. return 1 - shift (change necessary to exponent) public int DivAndShift(BigInt n2) { if (n2.IsZero()) return -1; if (digitArray.Length != n2.digitArray.Length) MakeSafe(ref n2); int oldLength = digitArray.Length; //Double the number of digits, and shift a into the higher digits. Pad(); n2.Extend(); //Do the divide (at double precision, ouch!) Div(n2); //Shift down if 'this' >= 2^d int ret = 1; if (digitArray[oldLength] != 0) { RSH(1); ret--; } SetNumDigits(oldLength); n2.SetNumDigits(oldLength); return ret; }
private static BigInt DigitTruncate(BigInt n1, int digits) { BigInt res = new BigInt(n1); for (int i = res.digitArray.Length - 1; i >= digits; i--) { res.digitArray[i] = 0; } return res; }
/// <summary> /// Calculates 'this'^power /// </summary> /// <param name="power"></param> public void Power(BigInt power) { if (power.IsZero() || power.sign) { Zero(); digitArray[0] = 1; return; } BigInt pow = new BigInt(power); BigInt temp = new BigInt(this); BigInt powTerm = new BigInt(this); pow.Decrement(); for (; !pow.IsZero(); pow.RSH(1)) { if ((pow.digitArray[0] & 1) == 1) { temp.Mul(powTerm); } powTerm.Square(); } Assign(temp); }
/// <summary> /// true iff n1 == n2 /// </summary> /// <param name="n1"></param> /// <param name="n2"></param> /// <returns></returns> public static bool Equals(BigInt n1, BigInt n2) { return n1.Equals(n2); }