/// <summary> /// Compares 2 <see cref="IntX" /> objects. /// Returns "-2" if any argument is null, "-1" if <paramref name="int1" /> < <paramref name="int2" />, /// "0" if equal and "1" if >. /// </summary> /// <param name="int1">First big integer.</param> /// <param name="int2">Second big integer.</param> /// <param name="throwNullException">Raises or not <see cref="NullReferenceException" />.</param> /// <returns>Comparsion result.</returns> /// <exception cref="ArgumentNullException"><paramref name="int1" /> or <paramref name="int2" /> is a null reference and <paramref name="throwNullException" /> is set to true.</exception> static public int Cmp(IntX int1, IntX int2, bool throwNullException) { // If one of the operands is null, throw exception or return -2 bool isNull1 = ReferenceEquals(int1, null); bool isNull2 = ReferenceEquals(int2, null); if (isNull1 || isNull2) { if (throwNullException) { throw new ArgumentNullException(isNull1 ? "int1" : "int2", Strings.CantBeNullCmp); } else { return(isNull1 && isNull2 ? 0 : -2); } } // Compare sign if (int1._negative && !int2._negative) { return(-1); } if (!int1._negative && int2._negative) { return(1); } // Compare presentation return(DigitOpHelper.Cmp(int1._digits, int1._length, int2._digits, int2._length) * (int1._negative ? -1 : 1)); }
/// <summary> /// Converts digits from internal representaion into given base. /// </summary> /// <param name="digits">Big integer digits.</param> /// <param name="length">Big integer length.</param> /// <param name="numberBase">Base to use for output.</param> /// <param name="outputLength">Calculated output length (will be corrected inside).</param> /// <returns>Conversion result (later will be transformed to string).</returns> override public uint[] ToString(uint[] digits, uint length, uint numberBase, ref uint outputLength) { uint[] outputArray = base.ToString(digits, length, numberBase, ref outputLength); // Maybe base method already converted this number if (outputArray != null) { return(outputArray); } // Create an output array for storing of number in other base outputArray = new uint[outputLength + 1]; // Make a copy of initial data uint[] digitsCopy = new uint[length]; Array.Copy(digits, digitsCopy, length); // Calculate output numbers by dividing uint outputIndex; for (outputIndex = 0; length > 0; ++outputIndex) { length = DigitOpHelper.DivMod(digitsCopy, length, numberBase, digitsCopy, out outputArray[outputIndex]); } outputLength = outputIndex; return(outputArray); }
/// <summary> /// Subtracts two big integers. /// </summary> /// <param name="int1">First big integer.</param> /// <param name="int2">Second big integer.</param> /// <returns>Resulting big integer.</returns> static public IntX Sub(IntX int1, IntX int2) { // Process zero values in special way if (int1._length == 0) { return(new IntX(int2._digits, true)); } if (int2._length == 0) { return(new IntX(int1)); } // Determine lower big int (without sign) IntX smallerInt; IntX biggerInt; int compareResult = DigitOpHelper.Cmp(int1._digits, int1._length, int2._digits, int2._length); if (compareResult == 0) { return(new IntX()); // integers are equal } if (compareResult < 0) { smallerInt = int1; biggerInt = int2; } else { smallerInt = int2; biggerInt = int1; } // Create new big int object IntX newInt = new IntX(biggerInt._length, ReferenceEquals(int1, smallerInt) ^ int1._negative); // Do actual subtraction newInt._length = DigitOpHelper.Sub( biggerInt._digits, biggerInt._length, smallerInt._digits, smallerInt._length, newInt._digits); // Normalization may be needed newInt.TryNormalize(); return(newInt); }
/// <summary> /// Adds two big integers. /// </summary> /// <param name="int1">First big integer.</param> /// <param name="int2">Second big integer.</param> /// <returns>Resulting big integer.</returns> /// <exception cref="ArgumentException"><paramref name="int1" /> or <paramref name="int2" /> is too big for add operation.</exception> static public IntX Add(IntX int1, IntX int2) { // Process zero values in special way if (int2._length == 0) { return(new IntX(int1)); } if (int1._length == 0) { IntX x = new IntX(int2); x._negative = int1._negative; // always get sign of the first big integer return(x); } // Determine big int with lower length IntX smallerInt; IntX biggerInt; DigitHelper.GetMinMaxLengthObjects(int1, int2, out smallerInt, out biggerInt); // Check for add operation possibility if (biggerInt._length == uint.MaxValue) { throw new ArgumentException(Strings.IntegerTooBig); } // Create new big int object of needed length IntX newInt = new IntX(biggerInt._length + 1, int1._negative); // Do actual addition newInt._length = DigitOpHelper.Add( biggerInt._digits, biggerInt._length, smallerInt._digits, smallerInt._length, newInt._digits); // Normalization may be needed newInt.TryNormalize(); return(newInt); }
/// <summary> /// Shifts <see cref="IntX" /> object. /// Determines which operation to use basing on shift sign. /// </summary> /// <param name="intX">Big integer.</param> /// <param name="shift">Bits count to shift.</param> /// <param name="toLeft">If true the shifting to the left.</param> /// <returns>Bitwise shift operation result.</returns> /// <exception cref="ArgumentNullException"><paramref name="intX" /> is a null reference.</exception> static public IntX Sh(IntX intX, long shift, bool toLeft) { // Exceptions if (ReferenceEquals(intX, null)) { throw new ArgumentNullException("intX", Strings.CantBeNullOne); } // Zero can't be shifted if (intX._length == 0) { return(new IntX()); } // Can't shift on zero value if (shift == 0) { return(new IntX(intX)); } // Determine real bits count and direction ulong bitCount; bool negativeShift; DigitHelper.ToUInt64WithSign(shift, out bitCount, out negativeShift); toLeft ^= negativeShift; // Get position of the most significant bit in intX and amount of bits in intX int msb = Bits.Msb(intX._digits[intX._length - 1]); ulong intXBitCount = (ulong)(intX._length - 1) * Constants.DigitBitCount + (ulong)msb + 1UL; // If shifting to the right and shift is too big then return zero if (!toLeft && bitCount >= intXBitCount) { if (intX.Negative) { return(-1); } return(new IntX()); } // Calculate new bit count ulong newBitCount = toLeft ? intXBitCount + bitCount : intXBitCount - bitCount; // If shifting to the left and shift is too big to fit in big integer, throw an exception if (toLeft && newBitCount > Constants.MaxBitCount) { throw new ArgumentException(Strings.IntegerTooBig, "intX"); } // Get exact length of new big integer (no normalize is ever needed here). // Create new big integer with given length uint newLength = (uint)(newBitCount / Constants.DigitBitCount + (newBitCount % Constants.DigitBitCount == 0 ? 0UL : 1UL)); IntX newInt = new IntX(newLength, intX._negative); // Get full and small shift values uint fullDigits = (uint)(bitCount / Constants.DigitBitCount); int smallShift = (int)(bitCount % Constants.DigitBitCount); // We can just copy (no shift) if small shift is zero if (smallShift == 0) { if (toLeft) { Array.Copy(intX._digits, 0, newInt._digits, fullDigits, intX._length); } else { Array.Copy(intX._digits, fullDigits, newInt._digits, 0, newLength); } } else { // Do copy with real shift in the needed direction if (toLeft) { DigitOpHelper.Shr(intX._digits, 0, intX._length, newInt._digits, fullDigits + 1, Constants.DigitBitCount - smallShift, false); } else { // If new result length is smaller then original length we shouldn't lose any digits if (newLength < intX._length) { newLength++; } DigitOpHelper.Shr(intX._digits, fullDigits, newLength, newInt._digits, 0, smallShift, fullDigits + newLength > intX._length); } } if (!toLeft && intX.Negative) { return(~-newInt); } return(newInt); }
/// <summary> /// Divides one <see cref="IntX" /> by another. /// </summary> /// <param name="int1">First big integer.</param> /// <param name="int2">Second big integer.</param> /// <param name="modRes">Remainder big integer.</param> /// <param name="resultFlags">Which operation results to return.</param> /// <returns>Divident big integer.</returns> /// <exception cref="ArgumentNullException"><paramref name="int1" /> or <paramref name="int2" /> is a null reference.</exception> /// <exception cref="DivideByZeroException"><paramref name="int2" /> equals zero.</exception> virtual public IntX DivMod(IntX int1, IntX int2, out IntX modRes, DivModResultFlags resultFlags) { // Null reference exceptions if (ReferenceEquals(int1, null)) { throw new ArgumentNullException("int1", Strings.CantBeNull); } else if (ReferenceEquals(int2, null)) { throw new ArgumentNullException("int2", Strings.CantBeNull); } // Check if int2 equals zero if (int2._length == 0) { throw new DivideByZeroException(); } // Get flags bool divNeeded = (resultFlags & DivModResultFlags.Div) != 0; bool modNeeded = (resultFlags & DivModResultFlags.Mod) != 0; // Special situation: check if int1 equals zero; in this case zero is always returned if (int1._length == 0) { modRes = modNeeded ? new IntX() : null; return(divNeeded ? new IntX() : null); } // Special situation: check if int2 equals one - nothing to divide in this case if (int2._length == 1 && int2._digits[0] == 1) { modRes = modNeeded ? new IntX() : null; return(divNeeded ? int2._negative ? -int1 : +int1 : null); } // Get resulting sign bool resultNegative = int1._negative ^ int2._negative; // Check if int1 > int2 int compareResult = DigitOpHelper.Cmp(int1._digits, int1._length, int2._digits, int2._length); if (compareResult < 0) { modRes = modNeeded ? new IntX(int1) : null; return(divNeeded ? new IntX() : null); } else if (compareResult == 0) { modRes = modNeeded ? new IntX() : null; return(divNeeded ? new IntX(resultNegative ? -1 : 1) : null); } // // Actually divide here (by Knuth algorithm) // // Prepare divident (if needed) IntX divRes = null; if (divNeeded) { divRes = new IntX(int1._length - int2._length + 1U, resultNegative); } // Prepare mod (if needed) if (modNeeded) { modRes = new IntX(int1._length + 1U, int1._negative); } else { modRes = null; } // Call procedure itself uint modLength = int1._length; uint divLength = DivMod( int1._digits, modNeeded ? modRes._digits : null, ref modLength, int2._digits, null, int2._length, divNeeded ? divRes._digits : null, resultFlags, compareResult); // Maybe set new lengths and perform normalization if (divNeeded) { divRes._length = divLength; divRes.TryNormalize(); } if (modNeeded) { modRes._length = modLength; modRes.TryNormalize(); } // Return div return(divRes); }
/// <summary> /// Divides two big integers. /// Also modifies <paramref name="digitsPtr1" /> and <paramref name="length1"/> (it will contain remainder). /// </summary> /// <param name="digitsPtr1">First big integer digits.</param> /// <param name="digitsBufferPtr1">Buffer for first big integer digits. May also contain remainder.</param> /// <param name="length1">First big integer length.</param> /// <param name="digitsPtr2">Second big integer digits.</param> /// <param name="digitsBufferPtr2">Buffer for second big integer digits. Only temporarily used.</param> /// <param name="length2">Second big integer length.</param> /// <param name="digitsResPtr">Resulting big integer digits.</param> /// <param name="resultFlags">Which operation results to return.</param> /// <param name="cmpResult">Big integers comparsion result (pass -2 if omitted).</param> /// <returns>Resulting big integer length.</returns> virtual unsafe public uint DivMod( uint *digitsPtr1, uint *digitsBufferPtr1, ref uint length1, uint *digitsPtr2, uint *digitsBufferPtr2, uint length2, uint *digitsResPtr, DivModResultFlags resultFlags, int cmpResult) { // Base implementation covers some special cases bool divNeeded = (resultFlags & DivModResultFlags.Div) != 0; bool modNeeded = (resultFlags & DivModResultFlags.Mod) != 0; // // Special cases // // Case when length1 == 0 if (length1 == 0) { return(0); } // Case when both lengths are 1 if (length1 == 1 && length2 == 1) { if (divNeeded) { *digitsResPtr = *digitsPtr1 / *digitsPtr2; if (*digitsResPtr == 0) { length2 = 0; } } if (modNeeded) { *digitsBufferPtr1 = *digitsPtr1 % *digitsPtr2; if (*digitsBufferPtr1 == 0) { length1 = 0; } } return(length2); } // Compare digits first (if was not previously compared) if (cmpResult == -2) { cmpResult = DigitOpHelper.Cmp(digitsPtr1, length1, digitsPtr2, length2); } // Case when first value is smaller then the second one - we will have remainder only if (cmpResult < 0) { // Maybe we should copy first digits into remainder (if remainder is needed at all) if (modNeeded) { DigitHelper.DigitsBlockCopy(digitsPtr1, digitsBufferPtr1, length1); } // Zero as division result return(0); } // Case when values are equal if (cmpResult == 0) { // Maybe remainder must be marked as empty if (modNeeded) { length1 = 0; } // One as division result if (divNeeded) { *digitsResPtr = 1; } return(1); } // Case when second length equals to 1 if (length2 == 1) { // Call method basing on fact if div is needed uint modRes; if (divNeeded) { length2 = DigitOpHelper.DivMod(digitsPtr1, length1, *digitsPtr2, digitsResPtr, out modRes); } else { modRes = DigitOpHelper.Mod(digitsPtr1, length1, *digitsPtr2); } // Maybe save mod result if (modNeeded) { if (modRes != 0) { length1 = 1; *digitsBufferPtr1 = modRes; } else { length1 = 0; } } return(length2); } // This is regular case, not special return(uint.MaxValue); }
/// <summary> /// Parses provided string representation of <see cref="IntX" /> object. /// </summary> /// <param name="value">Number as string.</param> /// <param name="startIndex">Index inside string from which to start.</param> /// <param name="endIndex">Index inside string on which to end.</param> /// <param name="numberBase">Number base.</param> /// <param name="charToDigits">Char->digit dictionary.</param> /// <param name="digitsRes">Resulting digits.</param> /// <returns>Parsed integer length.</returns> override unsafe public uint Parse(string value, int startIndex, int endIndex, uint numberBase, IDictionary <char, uint> charToDigits, uint[] digitsRes) { uint newLength = base.Parse(value, startIndex, endIndex, numberBase, charToDigits, digitsRes); // Maybe base method already parsed this number if (newLength != 0) { return(newLength); } // Check length - maybe use classic parser instead uint initialLength = (uint)digitsRes.LongLength; if (initialLength < Constants.FastParseLengthLowerBound || initialLength > Constants.FastParseLengthUpperBound) { return(_classicParser.Parse(value, startIndex, endIndex, numberBase, charToDigits, digitsRes)); } uint valueLength = (uint)(endIndex - startIndex + 1); uint digitsLength = 1U << Bits.CeilLog2(valueLength); // Prepare array for digits in other base uint[] valueDigits = ArrayPool <uint> .Instance.GetArray(digitsLength); // This second array will store integer lengths initially uint[] valueDigits2 = ArrayPool <uint> .Instance.GetArray(digitsLength); fixed(uint *valueDigitsStartPtr = valueDigits, valueDigitsStartPtr2 = valueDigits2) { // In the string first digit means last in digits array uint *valueDigitsPtr = valueDigitsStartPtr + valueLength - 1; uint *valueDigitsPtr2 = valueDigitsStartPtr2 + valueLength - 1; // Reverse copy characters into digits fixed(char *valueStartPtr = value) { char *valuePtr = valueStartPtr + startIndex; char *valueEndPtr = valuePtr + valueLength; for (; valuePtr < valueEndPtr; ++valuePtr, --valueDigitsPtr, --valueDigitsPtr2) { // Get digit itself - this call will throw an exception if char is invalid *valueDigitsPtr = StrRepHelper.GetDigit(charToDigits, *valuePtr, numberBase); // Set length of this digit (zero for zero) *valueDigitsPtr2 = *valueDigitsPtr == 0U ? 0U : 1U; } } // We have retrieved lengths array from pool - it needs to be cleared before using DigitHelper.SetBlockDigits(valueDigitsStartPtr2 + valueLength, digitsLength - valueLength, 0); // Now start from the digit arrays beginning valueDigitsPtr = valueDigitsStartPtr; valueDigitsPtr2 = valueDigitsStartPtr2; // Current multiplier (classic or fast) will be used IMultiplier multiplier = MultiplyManager.GetCurrentMultiplier(); // Here base in needed power will be stored IntX baseInt = null; // Temporary variables used on swapping uint[] tempDigits; uint * tempPtr; // Variables used in cycle uint *ptr1, ptr2, valueDigitsPtrEnd; uint loLength, hiLength; // Outer cycle instead of recursion for (uint innerStep = 1, outerStep = 2; innerStep < digitsLength; innerStep <<= 1, outerStep <<= 1) { // Maybe baseInt must be multiplied by itself baseInt = baseInt == null ? numberBase : baseInt * baseInt; // Using unsafe here fixed(uint *baseDigitsPtr = baseInt._digits) { // Start from arrays beginning ptr1 = valueDigitsPtr; ptr2 = valueDigitsPtr2; // vauleDigits array end valueDigitsPtrEnd = valueDigitsPtr + digitsLength; // Cycle thru all digits and their lengths for (; ptr1 < valueDigitsPtrEnd; ptr1 += outerStep, ptr2 += outerStep) { // Get lengths of "lower" and "higher" value parts loLength = *ptr2; hiLength = *(ptr2 + innerStep); if (hiLength != 0) { // We always must clear an array before multiply DigitHelper.SetBlockDigits(ptr2, outerStep, 0U); // Multiply per baseInt hiLength = multiplier.Multiply( baseDigitsPtr, baseInt._length, ptr1 + innerStep, hiLength, ptr2); } // Sum results if (hiLength != 0 || loLength != 0) { *ptr1 = DigitOpHelper.Add( ptr2, hiLength, ptr1, loLength, ptr2); } else { *ptr1 = 0U; } } } // After inner cycle valueDigits will contain lengths and valueDigits2 will contain actual values // so we need to swap them here tempDigits = valueDigits; valueDigits = valueDigits2; valueDigits2 = tempDigits; tempPtr = valueDigitsPtr; valueDigitsPtr = valueDigitsPtr2; valueDigitsPtr2 = tempPtr; } } // Determine real length of converted number uint realLength = valueDigits2[0]; // Copy to result Array.Copy(valueDigits, digitsRes, realLength); // Return arrays to pool ArrayPool <uint> .Instance.AddArray(valueDigits); ArrayPool <uint> .Instance.AddArray(valueDigits2); return(realLength); }