/// <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); }