/// <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> override unsafe public uint DivMod( uint *digitsPtr1, uint *digitsBufferPtr1, ref uint length1, uint *digitsPtr2, uint *digitsBufferPtr2, uint length2, uint *digitsResPtr, DivModResultFlags resultFlags, int cmpResult) { // Maybe immediately use classic algorithm here if (IsClassicAlgorithmNeeded(length1, length2)) { return(_classicDivider.DivMod( digitsPtr1, digitsBufferPtr1, ref length1, digitsPtr2, digitsBufferPtr2, length2, digitsResPtr, resultFlags, cmpResult)); } // Call base (for special cases) uint resultLength = base.DivMod( digitsPtr1, digitsBufferPtr1, ref length1, digitsPtr2, digitsBufferPtr2, length2, digitsResPtr, resultFlags, cmpResult); if (resultLength != uint.MaxValue) { return(resultLength); } // First retrieve opposite for the divider uint int2OppositeLength; ulong int2OppositeRightShift; uint[] int2OppositeDigits = NewtonHelper.GetIntegerOpposite( digitsPtr2, length2, length1, digitsBufferPtr1, out int2OppositeLength, out int2OppositeRightShift); // We will need to muptiply it by divident now to receive quotient. // Prepare digits for multiply result uint quotLength; uint[] quotDigits = new uint[length1 + int2OppositeLength]; IMultiplier multiplier = MultiplyManager.GetCurrentMultiplier(); // Fix some arrays fixed(uint *oppositePtr = int2OppositeDigits, quotPtr = quotDigits) { // Multiply quotLength = multiplier.Multiply( oppositePtr, int2OppositeLength, digitsPtr1, length1, quotPtr); // Calculate shift uint shiftOffset = (uint)(int2OppositeRightShift / Constants.DigitBitCount); int shiftCount = (int)(int2OppositeRightShift % Constants.DigitBitCount); // Get the very first bit of the shifted part uint highestLostBit; if (shiftCount == 0) { highestLostBit = quotPtr[shiftOffset - 1] >> 31; } else { highestLostBit = quotPtr[shiftOffset] >> (shiftCount - 1) & 1U; } // After this result must be shifted to the right - this is required quotLength = DigitOpHelper.Shr( quotPtr + shiftOffset, quotLength - shiftOffset, quotPtr, shiftCount, false); // Maybe quotient must be corrected if (highestLostBit == 1U) { quotLength = DigitOpHelper.Add(quotPtr, quotLength, &highestLostBit, 1U, quotPtr); } // Check quotient - finally it might be too big. // For this we must multiply quotient by divider uint quotDivLength; uint[] quotDivDigits = new uint[quotLength + length2]; fixed(uint *quotDivPtr = quotDivDigits) { quotDivLength = multiplier.Multiply(quotPtr, quotLength, digitsPtr2, length2, quotDivPtr); int cmpRes = DigitOpHelper.Cmp(quotDivPtr, quotDivLength, digitsPtr1, length1); if (cmpRes > 0) { highestLostBit = 1; quotLength = DigitOpHelper.Sub(quotPtr, quotLength, &highestLostBit, 1U, quotPtr); quotDivLength = DigitOpHelper.Sub(quotDivPtr, quotDivLength, digitsPtr2, length2, quotDivPtr); } // Now everything is ready and prepared to return results // First maybe fill remainder if ((resultFlags & DivModResultFlags.Mod) != 0) { length1 = DigitOpHelper.Sub(digitsPtr1, length1, quotDivPtr, quotDivLength, digitsBufferPtr1); } // And finally fill quotient if ((resultFlags & DivModResultFlags.Div) != 0) { DigitHelper.DigitsBlockCopy(quotPtr, digitsResPtr, quotLength); } else { quotLength = 0; } // Return some arrays to pool ArrayPool <uint> .Instance.AddArray(int2OppositeDigits); return(quotLength); } } }
/// <summary> /// Generates integer opposite to the given one using approximation. /// Uses algorithm from Khuth vol. 2 3rd Edition (4.3.3). /// </summary> /// <param name="digitsPtr">Initial big integer digits.</param> /// <param name="length">Initial big integer length.</param> /// <param name="maxLength">Precision length.</param> /// <param name="bufferPtr">Buffer in which shifted big integer may be stored.</param> /// <param name="newLength">Resulting big integer length.</param> /// <param name="rightShift">How much resulting big integer is shifted to the left (or: must be shifted to the right).</param> /// <returns>Resulting big integer digits.</returns> static unsafe public uint[] GetIntegerOpposite( uint *digitsPtr, uint length, uint maxLength, uint *bufferPtr, out uint newLength, out ulong rightShift) { // Maybe initially shift original digits a bit to the left // (it must have MSB on 2nd position in the highest digit) int msb = Bits.Msb(digitsPtr[length - 1]); rightShift = (ulong)(length - 1) * Constants.DigitBitCount + (ulong)msb + 1U; if (msb != 2) { // Shift to the left (via actually right shift) int leftShift = (2 - msb + Constants.DigitBitCount) % Constants.DigitBitCount; length = DigitOpHelper.Shr(digitsPtr, length, bufferPtr + 1, Constants.DigitBitCount - leftShift, true) + 1U; } else { // Simply use the same digits without any shifting bufferPtr = digitsPtr; } // Calculate possible result length int lengthLog2 = Bits.CeilLog2(maxLength); uint newLengthMax = 1U << (lengthLog2 + 1); int lengthLog2Bits = lengthLog2 + Bits.Msb(Constants.DigitBitCount); // Create result digits uint[] resultDigits = ArrayPool <uint> .Instance.GetArray(newLengthMax); //new uint[newLengthMax]; uint resultLength; // Create temporary digits for squared result (twice more size) uint[] resultDigitsSqr = ArrayPool <uint> .Instance.GetArray(newLengthMax); //new uint[newLengthMax]; uint resultLengthSqr; // Create temporary digits for squared result * buffer uint[] resultDigitsSqrBuf = new uint[newLengthMax + length]; uint resultLengthSqrBuf; // We will always use current multiplier IMultiplier multiplier = MultiplyManager.GetCurrentMultiplier(); // Fix some digits fixed(uint *resultPtrFixed = resultDigits, resultSqrPtrFixed = resultDigitsSqr, resultSqrBufPtr = resultDigitsSqrBuf) { uint *resultPtr = resultPtrFixed; uint *resultSqrPtr = resultSqrPtrFixed; // Cache two first digits uint bufferDigitN1 = bufferPtr[length - 1]; uint bufferDigitN2 = bufferPtr[length - 2]; // Prepare result. // Initially result = floor(32 / (4*v1 + 2*v2 + v3)) / 4 // (last division is not floored - here we emulate fixed point) resultDigits[0] = 32 / bufferDigitN1; resultLength = 1; // Prepare variables uint nextBufferTempStorage = 0; int nextBufferTempShift; uint nextBufferLength = 1U; uint *nextBufferPtr = &nextBufferTempStorage; ulong bitsAfterDotResult; ulong bitsAfterDotResultSqr; ulong bitsAfterDotNextBuffer; ulong bitShift; uint shiftOffset; uint *tempPtr; uint[] tempDigits; // Iterate 'till result will be precise enough for (int k = 0; k < lengthLog2Bits; ++k) { // Get result squared resultLengthSqr = multiplier.Multiply( resultPtr, resultLength, resultPtr, resultLength, resultSqrPtr); // Calculate current result bits after dot bitsAfterDotResult = (1UL << k) + 1UL; bitsAfterDotResultSqr = bitsAfterDotResult << 1; // Here we will get the next portion of data from bufferPtr if (k < 4) { // For now buffer intermediate has length 1 and we will use this fact nextBufferTempShift = 1 << (k + 1); nextBufferTempStorage = bufferDigitN1 << nextBufferTempShift | bufferDigitN2 >> (Constants.DigitBitCount - nextBufferTempShift); // Calculate amount of bits after dot (simple formula here) bitsAfterDotNextBuffer = (ulong)nextBufferTempShift + 3UL; } else { // Determine length to get from bufferPtr nextBufferLength = System.Math.Min((1U << (k - 4)) + 1U, length); nextBufferPtr = bufferPtr + (length - nextBufferLength); // Calculate amount of bits after dot (simple formula here) bitsAfterDotNextBuffer = (ulong)(nextBufferLength - 1U) * Constants.DigitBitCount + 3UL; } // Multiply result ^ 2 and nextBuffer + calculate new amount of bits after dot resultLengthSqrBuf = multiplier.Multiply( resultSqrPtr, resultLengthSqr, nextBufferPtr, nextBufferLength, resultSqrBufPtr); bitsAfterDotNextBuffer += bitsAfterDotResultSqr; // Now calculate 2 * result - resultSqrBufPtr --bitsAfterDotResult; --bitsAfterDotResultSqr; // Shift result on a needed amount of bits to the left bitShift = bitsAfterDotResultSqr - bitsAfterDotResult; shiftOffset = (uint)(bitShift / Constants.DigitBitCount); resultLength = shiftOffset + 1U + DigitOpHelper.Shr( resultPtr, resultLength, resultSqrPtr + shiftOffset + 1U, Constants.DigitBitCount - (int)(bitShift % Constants.DigitBitCount), true); // Swap resultPtr and resultSqrPtr pointers tempPtr = resultPtr; resultPtr = resultSqrPtr; resultSqrPtr = tempPtr; tempDigits = resultDigits; resultDigits = resultDigitsSqr; resultDigitsSqr = tempDigits; DigitHelper.SetBlockDigits(resultPtr, shiftOffset, 0U); bitShift = bitsAfterDotNextBuffer - bitsAfterDotResultSqr; shiftOffset = (uint)(bitShift / Constants.DigitBitCount); if (shiftOffset < resultLengthSqrBuf) { // Shift resultSqrBufPtr on a needed amount of bits to the right resultLengthSqrBuf = DigitOpHelper.Shr( resultSqrBufPtr + shiftOffset, resultLengthSqrBuf - shiftOffset, resultSqrBufPtr, (int)(bitShift % Constants.DigitBitCount), false); // Now perform actual subtraction resultLength = DigitOpHelper.Sub( resultPtr, resultLength, resultSqrBufPtr, resultLengthSqrBuf, resultPtr); } else { // Actually we can assume resultSqrBufPtr == 0 here and have nothing to do } } } // Return some arrays to pool ArrayPool <uint> .Instance.AddArray(resultDigitsSqr); rightShift += (1UL << lengthLog2Bits) + 1UL; newLength = resultLength; return(resultDigits); }
/// <summary> /// Multiplies two big integers using pointers. /// </summary> /// <param name="digitsPtr1">First big integer digits.</param> /// <param name="length1">First big integer length.</param> /// <param name="digitsPtr2">Second big integer digits.</param> /// <param name="length2">Second big integer length.</param> /// <param name="digitsResPtr">Resulting big integer digits.</param> /// <returns>Resulting big integer real length.</returns> override unsafe public uint Multiply(uint *digitsPtr1, uint length1, uint *digitsPtr2, uint length2, uint *digitsResPtr) { // Check length - maybe use classic multiplier instead if (length1 < Constants.AutoFhtLengthLowerBound || length2 < Constants.AutoFhtLengthLowerBound || length1 > Constants.AutoFhtLengthUpperBound || length2 > Constants.AutoFhtLengthUpperBound) { return(_classicMultiplier.Multiply(digitsPtr1, length1, digitsPtr2, length2, digitsResPtr)); } uint newLength = length1 + length2; // Do FHT for first big integer double[] data1 = FhtHelper.ConvertDigitsToDouble(digitsPtr1, length1, newLength); FhtHelper.Fht(data1, (uint)data1.LongLength); // Compare digits double[] data2; if (digitsPtr1 == digitsPtr2 || DigitOpHelper.Cmp(digitsPtr1, length1, digitsPtr2, length2) == 0) { // Use the same FHT for equal big integers data2 = data1; } else { // Do FHT over second digits data2 = FhtHelper.ConvertDigitsToDouble(digitsPtr2, length2, newLength); FhtHelper.Fht(data2, (uint)data2.LongLength); } // Perform multiplication and reverse FHT FhtHelper.MultiplyFhtResults(data1, data2, (uint)data1.LongLength); FhtHelper.ReverseFht(data1, (uint)data1.LongLength); // Convert to digits fixed(double *slice1 = data1) { FhtHelper.ConvertDoubleToDigits(slice1, (uint)data1.LongLength, newLength, digitsResPtr); } // Return double arrays back to pool ArrayPool <double> .Instance.AddArray(data1); if (data2 != data1) { ArrayPool <double> .Instance.AddArray(data2); } // Maybe check for validity using classic multiplication if (IntX.GlobalSettings.ApplyFhtValidityCheck) { uint lowerDigitCount = System.Math.Min(length2, System.Math.Min(length1, Constants.FhtValidityCheckDigitCount)); // Validate result by multiplying lowerDigitCount digits using classic algorithm and comparing uint[] validationResult = new uint[lowerDigitCount * 2]; fixed(uint *validationResultPtr = validationResult) { _classicMultiplier.Multiply(digitsPtr1, lowerDigitCount, digitsPtr2, lowerDigitCount, validationResultPtr); if (DigitOpHelper.Cmp(validationResultPtr, lowerDigitCount, digitsResPtr, lowerDigitCount) != 0) { throw new FhtMultiplicationException(string.Format(Strings.FhtMultiplicationError, length1, length2)); } } } return(digitsResPtr[newLength - 1] == 0 ? --newLength : newLength); }
/// <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> override unsafe public uint DivMod( uint *digitsPtr1, uint *digitsBufferPtr1, ref uint length1, uint *digitsPtr2, uint *digitsBufferPtr2, uint length2, uint *digitsResPtr, DivModResultFlags resultFlags, int cmpResult) { // Call base (for special cases) uint resultLength = base.DivMod( digitsPtr1, digitsBufferPtr1, ref length1, digitsPtr2, digitsBufferPtr2, length2, digitsResPtr, resultFlags, cmpResult); if (resultLength != uint.MaxValue) { return(resultLength); } bool divNeeded = (resultFlags & DivModResultFlags.Div) != 0; bool modNeeded = (resultFlags & DivModResultFlags.Mod) != 0; // // Prepare digitsBufferPtr1 and digitsBufferPtr2 // int shift1 = 31 - Bits.Msb(digitsPtr2[length2 - 1]); if (shift1 == 0) { // We don't need to shift - just copy DigitHelper.DigitsBlockCopy(digitsPtr1, digitsBufferPtr1, length1); // We also don't need to shift second digits digitsBufferPtr2 = digitsPtr2; } else { int rightShift1 = Constants.DigitBitCount - shift1; // We do need to shift here - so copy with shift - suppose we have enough storage for this operation length1 = DigitOpHelper.Shr(digitsPtr1, length1, digitsBufferPtr1 + 1, rightShift1, true) + 1U; // Second digits also must be shifted DigitOpHelper.Shr(digitsPtr2, length2, digitsBufferPtr2 + 1, rightShift1, true); } // // Division main algorithm implementation // ulong longDigit; ulong divEst; ulong modEst; ulong mulRes; uint divRes; long k, t; // Some digits2 cached digits uint lastDigit2 = digitsBufferPtr2[length2 - 1]; uint preLastDigit2 = digitsBufferPtr2[length2 - 2]; // Main divide loop bool isMaxLength; uint maxLength = length1 - length2; for (uint i = maxLength, iLen2 = length1, j, ji; i <= maxLength; --i, --iLen2) { isMaxLength = iLen2 == length1; // Calculate estimates if (isMaxLength) { longDigit = digitsBufferPtr1[iLen2 - 1]; } else { longDigit = (ulong)digitsBufferPtr1[iLen2] << Constants.DigitBitCount | digitsBufferPtr1[iLen2 - 1]; } divEst = longDigit / lastDigit2; modEst = longDigit - divEst * lastDigit2; // Check estimate (maybe correct it) for (;;) { if (divEst == Constants.BitCountStepOf2 || divEst * preLastDigit2 > (modEst << Constants.DigitBitCount) + digitsBufferPtr1[iLen2 - 2]) { --divEst; modEst += lastDigit2; if (modEst < Constants.BitCountStepOf2) { continue; } } break; } divRes = (uint)divEst; // Multiply and subtract k = 0; for (j = 0, ji = i; j < length2; ++j, ++ji) { mulRes = (ulong)divRes * digitsBufferPtr2[j]; t = digitsBufferPtr1[ji] - k - (long)(mulRes & 0xFFFFFFFF); digitsBufferPtr1[ji] = (uint)t; k = (long)(mulRes >> Constants.DigitBitCount) - (t >> Constants.DigitBitCount); } if (!isMaxLength) { t = digitsBufferPtr1[iLen2] - k; digitsBufferPtr1[iLen2] = (uint)t; } else { t = -k; } // Correct result if subtracted too much if (t < 0) { --divRes; k = 0; for (j = 0, ji = i; j < length2; ++j, ++ji) { t = (long)digitsBufferPtr1[ji] + digitsBufferPtr2[j] + k; digitsBufferPtr1[ji] = (uint)t; k = t >> Constants.DigitBitCount; } if (!isMaxLength) { digitsBufferPtr1[iLen2] = (uint)(k + digitsBufferPtr1[iLen2]); } } // Maybe save div result if (divNeeded) { digitsResPtr[i] = divRes; } } if (modNeeded) { // First set correct mod length length1 = DigitHelper.GetRealDigitsLength(digitsBufferPtr1, length2); // Next maybe shift result back to the right if (shift1 != 0 && length1 != 0) { length1 = DigitOpHelper.Shr(digitsBufferPtr1, length1, digitsBufferPtr1, shift1, false); } } // Finally return length return(!divNeeded ? 0 : (digitsResPtr[maxLength] == 0 ? maxLength : ++maxLength)); }