Beispiel #1
0
        /// <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);
                }
            }
        }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
        /// <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);
        }
Beispiel #4
0
        /// <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));
        }