/* Copyright (c) 2005-2010, Andriy Kozachuk a.k.a. Oyster All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Andriy Kozachuk nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PLEASE NOTE: This file has been heavily modified from it's original version for use in this library. */ using System; using System.Text.RegularExpressions; using System.Collections; using System.Text; using System.Collections.Generic; namespace System { /// /// Numeric class which represents arbitrary-precision integers. /// (c) Andriy Kozachuk a.k.a. Oyster [dev.oyster@gmail.com] 2005-2010 /// public sealed class IntX : IEquatable, IEquatable, IEquatable, IEquatable, IEquatable, IComparable, IComparable, IComparable, IComparable, IComparable, IComparable, IDisposable, ICloneable { #region Internal Classes #region IntXDivider internal static class IntXDivider { /// /// Returns true if it's better to use classic algorithm for given big integers. /// /// First big integer length. /// Second big integer length. /// True if classic algorithm is better. private static bool IsClassicAlgorithmNeeded(uint length1, uint length2) { return length1 < Constants.AutoNewtonLengthLowerBound || length2 < Constants.AutoNewtonLengthLowerBound || length1 > Constants.AutoNewtonLengthUpperBound || length2 > Constants.AutoNewtonLengthUpperBound; } /// /// Divides two big integers. /// Also modifies and (it will contain remainder). /// /// First big integer digits. /// Buffer for first big integer digits. May also contain remainder. Can be null - in this case it's created if necessary. /// First big integer length. /// Second big integer digits. /// Buffer for second big integer digits. Only temporarily used. Can be null - in this case it's created if necessary. /// Second big integer length. /// Resulting big integer digits. /// Which operation results to return. /// Big integers comparsion result (pass -2 if omitted). /// Resulting big integer length. public static unsafe uint DivMod(uint[] digits1, uint[] digitsBuffer1, ref uint length1, uint[] digits2, uint[] digitsBuffer2, uint length2, uint[] digitsRes, DivModResultFlags resultFlags, int cmpResult) { // Maybe immediately use classic algorithm here if (IsClassicAlgorithmNeeded(length1, length2)) { return ClassicDivMod(digits1, digitsBuffer1, ref length1, digits2, digitsBuffer2, length2, digitsRes, resultFlags, cmpResult); } // Create some buffers if necessary if (digitsBuffer1 == null) { digitsBuffer1 = new uint[length1 + 1]; } fixed (uint* digitsPtr1 = digits1, digitsBufferPtr1 = digitsBuffer1, digitsPtr2 = digits2, digitsBufferPtr2 = digitsBuffer2 != null ? digitsBuffer2 : digits1, digitsResPtr = digitsRes != null ? digitsRes : digits1) { return DivMod( digitsPtr1, digitsBufferPtr1, ref length1, digitsPtr2, digitsBufferPtr2 == digitsPtr1 ? null : digitsBufferPtr2, length2, digitsResPtr == digitsPtr1 ? null : digitsResPtr, resultFlags, cmpResult); } } /// /// Divides two big integers. /// Also modifies and (it will contain remainder). /// /// First big integer digits. /// Buffer for first big integer digits. May also contain remainder. /// First big integer length. /// Second big integer digits. /// Buffer for second big integer digits. Only temporarily used. /// Second big integer length. /// Resulting big integer digits. /// Which operation results to return. /// Big integers comparsion result (pass -2 if omitted). /// Resulting big integer length. public static unsafe 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 ClassicDivMod(digitsPtr1, digitsBufferPtr1, ref length1, digitsPtr2, digitsBufferPtr2, length2, digitsResPtr, resultFlags, cmpResult); } // Call base (for special cases) uint resultLength = BaseDivMod(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]; // Fix some arrays fixed (uint* oppositePtr = int2OppositeDigits, quotPtr = quotDigits) { // Multiply quotLength = IntXMultiplier.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 = IntXMultiplier.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.Instance.AddArray(int2OppositeDigits); return quotLength; } } } /// /// Divides one by another. /// /// First big integer. /// Second big integer. /// Remainder big integer. /// Which operation results to return. /// Divident big integer. /// or is a null reference. /// equals zero. public static IntX DivMod(IntX int1, IntX int2, out IntX modRes, DivModResultFlags resultFlags) { // Null reference exceptions if (ReferenceEquals(int1, null)) { throw new ArgumentNullException("int1", "Operands can't be null."); } else if (ReferenceEquals(int2, null)) { throw new ArgumentNullException("int2", "Operands can't be null."); } // 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; } /// /// Divides two big integers. /// Also modifies and (it will contain remainder). /// /// First big integer digits. /// Buffer for first big integer digits. May also contain remainder. Can be null - in this case it's created if necessary. /// First big integer length. /// Second big integer digits. /// Buffer for second big integer digits. Only temporarily used. Can be null - in this case it's created if necessary. /// Second big integer length. /// Resulting big integer digits. /// Which operation results to return. /// Big integers comparsion result (pass -2 if omitted). /// Resulting big integer length. public static unsafe uint ClassicDivMod(uint[] digits1, uint[] digitsBuffer1, ref uint length1, uint[] digits2, uint[] digitsBuffer2, uint length2, uint[] digitsRes, DivModResultFlags resultFlags, int cmpResult) { // Create some buffers if necessary if (digitsBuffer1 == null) { digitsBuffer1 = new uint[length1 + 1]; } if (digitsBuffer2 == null) { digitsBuffer2 = new uint[length2]; } fixed (uint* digitsPtr1 = digits1, digitsBufferPtr1 = digitsBuffer1, digitsPtr2 = digits2, digitsBufferPtr2 = digitsBuffer2, digitsResPtr = digitsRes != null ? digitsRes : digits1) { return DivMod( digitsPtr1, digitsBufferPtr1, ref length1, digitsPtr2, digitsBufferPtr2, length2, digitsResPtr == digitsPtr1 ? null : digitsResPtr, resultFlags, cmpResult); } } /// /// Divides two big integers. /// Also modifies and (it will contain remainder). /// /// First big integer digits. /// Buffer for first big integer digits. May also contain remainder. /// First big integer length. /// Second big integer digits. /// Buffer for second big integer digits. Only temporarily used. /// Second big integer length. /// Resulting big integer digits. /// Which operation results to return. /// Big integers comparsion result (pass -2 if omitted). /// Resulting big integer length. public static unsafe uint ClassicDivMod(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 = BaseDivMod(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); } /// /// Divides two big integers. /// Also modifies and (it will contain remainder). /// /// First big integer digits. /// Buffer for first big integer digits. May also contain remainder. /// First big integer length. /// Second big integer digits. /// Buffer for second big integer digits. Only temporarily used. /// Second big integer length. /// Resulting big integer digits. /// Which operation results to return. /// Big integers comparsion result (pass -2 if omitted). /// Resulting big integer length. public static unsafe uint BaseDivMod(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; } } #endregion #region IntXMultiplier internal static class IntXMultiplier { /// /// Multiplies two big integers. /// /// First big integer. /// Second big integer. /// Resulting big integer. /// or is a null reference. /// or is too big for multiply operation. public static IntX Multiply(IntX int1, IntX int2) { // Exceptions if (ReferenceEquals(int1, null)) { throw new ArgumentNullException("int1", "Operands can't be null."); } else if (ReferenceEquals(int2, null)) { throw new ArgumentNullException("int2", "Operands can't be null."); } // Special behavior for zero cases if (int1._length == 0 || int2._length == 0) return new IntX(); // Get new big integer length and check it ulong newLength = (ulong)int1._length + int2._length; if (newLength >> 32 != 0) { throw new ArgumentException("One of the operated big integers is too big."); } // Create resulting big int IntX newInt = new IntX((uint)newLength, int1._negative ^ int2._negative); // Perform actual digits multiplication newInt._length = Multiply(int1._digits, int1._length, int2._digits, int2._length, newInt._digits); // Normalization may be needed newInt.TryNormalize(); return newInt; } /// /// Multiplies two big integers represented by their digits. /// /// First big integer digits. /// First big integer real length. /// Second big integer digits. /// Second big integer real length. /// Where to put resulting big integer. /// Resulting big integer real length. private static unsafe uint Multiply(uint[] digits1, uint length1, uint[] digits2, uint length2, uint[] digitsRes) { fixed (uint* digitsPtr1 = digits1, digitsPtr2 = digits2, digitsResPtr = digitsRes) { return Multiply(digitsPtr1, length1, digitsPtr2, length2, digitsResPtr); } } /// /// Multiplies two big integers using pointers. /// /// First big integer digits. /// First big integer length. /// Second big integer digits. /// Second big integer length. /// Resulting big integer digits. /// Resulting big integer real length. internal static unsafe 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 ClassicMultiply(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.Instance.AddArray(data1); if (data2 != data1) { ArrayPool.Instance.AddArray(data2); } //// Maybe check for validity using classic multiplication //if (System.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) // { // ClassicMultiply(digitsPtr1, lowerDigitCount, digitsPtr2, lowerDigitCount, validationResultPtr); // if (DigitOpHelper.Cmp(validationResultPtr, lowerDigitCount, digitsResPtr, lowerDigitCount) != 0) // { // throw new FhtMultiplicationException(string.Format("FHT multiplication returned invalid result for IntX objects with lengths {0} and {1}.", length1, length2)); // } // } //} return digitsResPtr[newLength - 1] == 0 ? --newLength : newLength; } /// /// Multiplies two big integers using pointers. /// /// First big integer digits. /// First big integer length. /// Second big integer digits. /// Second big integer length. /// Resulting big integer digits. /// Resulting big integer length. private static unsafe uint ClassicMultiply(uint* digitsPtr1, uint length1, uint* digitsPtr2, uint length2, uint* digitsResPtr) { ulong c; // External cycle must be always smaller if (length1 < length2) { // First must be bigger - swap uint lengthTemp = length1; length1 = length2; length2 = lengthTemp; uint* ptrTemp = digitsPtr1; digitsPtr1 = digitsPtr2; digitsPtr2 = ptrTemp; } // Prepare end pointers uint* digitsPtr1End = digitsPtr1 + length1; uint* digitsPtr2End = digitsPtr2 + length2; // We must always clear first "length1" digits in result DigitHelper.SetBlockDigits(digitsResPtr, length1, 0U); // Perform digits multiplication uint* ptr1, ptrRes = null; for (; digitsPtr2 < digitsPtr2End; ++digitsPtr2, ++digitsResPtr) { // Check for zero (sometimes may help). There is no sense to make this check in internal cycle - // it would give performance gain only here if (*digitsPtr2 == 0) continue; c = 0; for (ptr1 = digitsPtr1, ptrRes = digitsResPtr; ptr1 < digitsPtr1End; ++ptr1, ++ptrRes) { c += (ulong)*digitsPtr2 * *ptr1 + *ptrRes; *ptrRes = (uint)c; c >>= 32; } *ptrRes = (uint)c; } uint newLength = length1 + length2; if (newLength > 0 && (ptrRes == null || *ptrRes == 0)) { --newLength; } return newLength; } } #endregion #region IntXParser internal static class IntXParser { /// /// Regex pattern used on parsing stage to determine number sign and/or base /// private const string ParseRegexPattern = "(?[+-]?)((?0[Xx])|(?0))?"; /// /// Regex object used on parsing stage to determine number sign and/or base /// private static readonly Regex ParseRegex = new Regex(ParseRegexPattern, RegexOptions.Compiled); #region Pow2Parse /// /// Parses provided string representation of object. /// /// Number as string. /// Index inside string from which to start. /// Index inside string on which to end. /// Number base. /// Char->digit dictionary. /// Resulting digits. /// Parsed integer length. private static uint Pow2Parse(string value, int startIndex, int endIndex, uint numberBase, IDictionary charToDigits, uint[] digitsRes) { // Calculate length of input string int bitsInChar = Bits.Msb(numberBase); uint valueLength = (uint)(endIndex - startIndex + 1); ulong valueBitLength = (ulong)valueLength * (ulong)bitsInChar; // Calculate needed digits length and first shift uint digitsLength = (uint)(valueBitLength / Constants.DigitBitCount) + 1; uint digitIndex = digitsLength - 1; int initialShift = (int)(valueBitLength % Constants.DigitBitCount); // Probably correct digits length if (initialShift == 0) { --digitsLength; } // Do parsing in big cycle uint digit; for (int i = startIndex; i <= endIndex; ++i) { digit = StrRepHelper.GetDigit(charToDigits, value[i], numberBase); // Correct initial digit shift if (initialShift == 0) { // If shift is equals to zero then char is not on digit elements bounds, // so just go to the previous digit initialShift = Constants.DigitBitCount - bitsInChar; --digitIndex; } else { // Here shift might be negative, but it's okay initialShift -= bitsInChar; } // Insert new digit in correct place digitsRes[digitIndex] |= initialShift < 0 ? digit >> -initialShift : digit << initialShift; // In case if shift was negative we also must modify previous digit if (initialShift < 0) { initialShift += Constants.DigitBitCount; digitsRes[--digitIndex] |= digit << initialShift; } } if (digitsRes[digitsLength - 1] == 0) { --digitsLength; } return digitsLength; } #endregion #region BaseParse /// /// Parses provided string representation of object. /// /// Number as string. /// Number base. /// Char->digit dictionary. /// Check actual format of number (0 or 0x at start). /// Parsed object. /// is a null reference. /// is less then 2 or more then 16. /// is not in valid format. public static IntX Parse(string value, uint numberBase, IDictionary charToDigits, bool checkFormat) { // Exceptions if (value == null) { throw new ArgumentNullException("value"); } if (charToDigits == null) { throw new ArgumentNullException("charToDigits"); } if (numberBase < 2 || numberBase > charToDigits.Count) { throw new ArgumentException("Base is invalid.", "numberBase"); } // Initially determine start and end indices (Trim is too slow) int startIndex = 0; for (; startIndex < value.Length && char.IsWhiteSpace(value[startIndex]); ++startIndex) ; int endIndex = value.Length - 1; for (; endIndex >= startIndex && char.IsWhiteSpace(value[endIndex]); --endIndex) ; bool negative = false; // number sign bool stringNotEmpty = false; // true if string is already guaranteed to be non-empty // Determine sign and/or base Match match = ParseRegex.Match(value, startIndex, endIndex - startIndex + 1); if (match.Groups["Sign"].Value == "-") { negative = true; } if (match.Groups["BaseHex"].Length != 0) { if (checkFormat) { // 0x before the number - this is hex number numberBase = 16U; } else { // This is an error throw new FormatException("Invalid character in input."); } } else if (match.Groups["BaseOct"].Length != 0) { if (checkFormat) { // 0 before the number - this is octal number numberBase = 8U; } stringNotEmpty = true; } // Skip leading sign and format startIndex += match.Length; // If on this stage string is empty, this may mean an error if (startIndex > endIndex && !stringNotEmpty) { throw new FormatException("No digits in string."); } // Iterate thru string and skip all leading zeroes for (; startIndex <= endIndex && value[startIndex] == '0'; ++startIndex) ; // Return zero if length is zero if (startIndex > endIndex) return new IntX(); // Determine length of new digits array and create new IntX object with given length int valueLength = endIndex - startIndex + 1; uint digitsLength = (uint)System.Math.Ceiling(System.Math.Log(numberBase) / Constants.DigitBaseLog * valueLength); IntX newInt = new IntX(digitsLength, negative); // Now we have only (in)valid string which consists from numbers only. // Parse it newInt._length = Parse(value, startIndex, endIndex, numberBase, charToDigits, newInt._digits); return newInt; } /// /// Parses provided string representation of object. /// /// Number as string. /// Index inside string from which to start. /// Index inside string on which to end. /// Number base. /// Char->digit dictionary. /// Resulting digits. /// Parsed integer length. private static uint BaseParse(string value, int startIndex, int endIndex, uint numberBase, IDictionary charToDigits, uint[] digitsRes) { // Default implementation - always call pow2 parser if numberBase is pow of 2 return numberBase == 1U << Bits.Msb(numberBase) ? Pow2Parse(value, startIndex, endIndex, numberBase, charToDigits, digitsRes) : 0; } #endregion #region FastParser /// /// Parses provided string representation of object. /// /// Number as string. /// Index inside string from which to start. /// Index inside string on which to end. /// Number base. /// Char->digit dictionary. /// Resulting digits. /// Parsed integer length. private static unsafe uint Parse(string value, int startIndex, int endIndex, uint numberBase, IDictionary charToDigits, uint[] digitsRes) { uint newLength = BaseParse(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 ClassicParse(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.Instance.GetArray(digitsLength); // This second array will store integer lengths initially uint[] valueDigits2 = ArrayPool.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; // 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 = IntXMultiplier.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.Instance.AddArray(valueDigits); ArrayPool.Instance.AddArray(valueDigits2); return realLength; } #endregion #region ClassicParser /// /// Parses provided string representation of object. /// /// Number as string. /// Index inside string from which to start. /// Index inside string on which to end. /// Number base. /// Char->digit dictionary. /// Resulting digits. /// Parsed integer length. private static uint ClassicParse(string value, int startIndex, int endIndex, uint numberBase, IDictionary charToDigits, uint[] digitsRes) { uint newLength = BaseParse(value, startIndex, endIndex, numberBase, charToDigits, digitsRes); // Maybe base method already parsed this number if (newLength != 0) return newLength; // Do parsing in big cycle ulong numberBaseLong = numberBase; ulong digit; for (int i = startIndex; i <= endIndex; ++i) { digit = StrRepHelper.GetDigit(charToDigits, value[i], numberBase); // Next multiply existing values by base and add this value to them if (newLength == 0) { if (digit != 0) { digitsRes[0] = (uint)digit; newLength = 1; } } else { for (uint j = 0; j < newLength; ++j) { digit += digitsRes[j] * numberBaseLong; digitsRes[j] = (uint)digit; digit >>= 32; } if (digit != 0) { digitsRes[newLength++] = (uint)digit; } } } return newLength; } #endregion } #endregion #region IntXStringConverter internal static class IntXStringConverter { /// /// Returns string representation of object in given base. /// /// Big integer to convert. /// Base of system in which to do output. /// Alphabet which contains chars used to represent big integer, char position is coresponding digit value. /// Object string representation. /// is less then 2 or is too big to fit in string. public static string ToString(IntX intX, uint numberBase, char[] alphabet) { // Test base if (numberBase < 2 || numberBase > 65536) { throw new ArgumentException("Base must be between 2 and 65536.", "numberBase"); } // Special processing for zero values if (intX._length == 0) return "0"; // Calculate output array length uint outputLength = (uint)System.Math.Ceiling(Constants.DigitBaseLog / System.Math.Log(numberBase) * intX._length); // Define length coefficient for string builder bool isBigBase = numberBase > alphabet.Length; uint lengthCoef = isBigBase ? (uint)System.Math.Ceiling(System.Math.Log10(numberBase)) + 2U : 1U; // Determine maximal possible length of string ulong maxBuilderLength = (ulong)outputLength * lengthCoef + 1UL; if (maxBuilderLength > int.MaxValue) { // This big integer can't be transformed to string throw new ArgumentException("One of the operated big integers is too big.", "intX"); } // Transform digits into another base uint[] outputArray = FastToString(intX._digits, intX._length, numberBase, ref outputLength); // Output everything to the string builder StringBuilder outputBuilder = new StringBuilder((int)(outputLength * lengthCoef + 1)); // Maybe append minus sign if (intX._negative) { outputBuilder.Append(Constants.DigitsMinusChar); } // Output all digits for (uint i = outputLength - 1; i < outputLength; --i) { if (!isBigBase) { // Output char-by-char for bases up to covered by alphabet outputBuilder.Append(alphabet[(int)outputArray[i]]); } else { // Output digits in brackets for bigger bases outputBuilder.Append(Constants.DigitOpeningBracet); outputBuilder.Append(outputArray[i].ToString()); outputBuilder.Append(Constants.DigitClosingBracet); } } return outputBuilder.ToString(); } #region BaseToString /// /// Converts digits from internal representation into given base. /// /// Big integer digits. /// Big integer length. /// Base to use for output. /// Calculated output length (will be corrected inside). /// Conversion result (later will be transformed to string). private static uint[] BaseToString(uint[] digits, uint length, uint numberBase, ref uint outputLength) { // Default implementation - always call pow2 converter if numberBase is power of 2 return numberBase == 1U << Bits.Msb(numberBase) ? Pow2ToString(digits, length, numberBase, ref outputLength) : null; } #endregion #region FastToString /// /// Converts digits from internal representation into given base. /// /// Big integer digits. /// Big integer length. /// Base to use for output. /// Calculated output length (will be corrected inside). /// Conversion result (later will be transformed to string). private static unsafe uint[] FastToString(uint[] digits, uint length, uint numberBase, ref uint outputLength) { uint[] outputArray = BaseToString(digits, length, numberBase, ref outputLength); // Maybe base method already converted this number if (outputArray != null) return outputArray; // Check length - maybe use classic converter instead if (length < Constants.FastConvertLengthLowerBound || length > Constants.FastConvertLengthUpperBound) { return ClassicToString(digits, length, numberBase, ref outputLength); } int resultLengthLog2 = Bits.CeilLog2(outputLength); uint resultLength = 1U << resultLengthLog2; // Create and initially fill array for transformed numbers storing uint[] resultArray = ArrayPool.Instance.GetArray(resultLength); Array.Copy(digits, resultArray, length); // Create and initially fill array with lengths uint[] resultArray2 = ArrayPool.Instance.GetArray(resultLength); resultArray2[0] = length; // Generate all needed powers of numberBase in stack Stack baseIntStack = new Stack(resultLengthLog2); IntX baseInt = null; for (int i = 0; i < resultLengthLog2; ++i) { baseInt = baseInt == null ? numberBase : IntXMultiplier.Multiply(baseInt, baseInt); baseIntStack.Push(baseInt); } // Create temporary buffer for second digits when doing div operation uint[] tempBuffer = new uint[baseInt._length]; // We will use unsafe code here fixed (uint* resultPtr1Const = resultArray, resultPtr2Const = resultArray2, tempBufferPtr = tempBuffer) { // Results pointers which will be modified (on swap) uint* resultPtr1 = resultPtr1Const; uint* resultPtr2 = resultPtr2Const; // Temporary variables used on swapping uint[] tempArray; uint* tempPtr; // Variables used in cycle uint* ptr1, ptr2, ptr1end; uint loLength; // Outer cycle instead of recursion for (uint innerStep = resultLength >> 1, outerStep = resultLength; innerStep > 0; innerStep >>= 1, outerStep >>= 1) { // Prepare pointers ptr1 = resultPtr1; ptr2 = resultPtr2; ptr1end = resultPtr1 + resultLength; // Get baseInt from stack and fix it too baseInt = (IntX)baseIntStack.Pop(); fixed (uint* baseIntPtr = baseInt._digits) { // Cycle thru all digits and their lengths for (; ptr1 < ptr1end; ptr1 += outerStep, ptr2 += outerStep) { // Divide ptr1 (with length in *ptr2) by baseIntPtr here. // Results are stored in ptr2 & (ptr2 + innerStep), lengths - in *ptr1 and (*ptr1 + innerStep) loLength = *ptr2; *(ptr1 + innerStep) = IntXDivider.DivMod( ptr1, ptr2, ref loLength, baseIntPtr, tempBufferPtr, baseInt._length, ptr2 + innerStep, DivModResultFlags.Div | DivModResultFlags.Mod, -2); *ptr1 = loLength; } } // After inner cycle resultArray will contain lengths and resultArray2 will contain actual values // so we need to swap them here tempArray = resultArray; resultArray = resultArray2; resultArray2 = tempArray; tempPtr = resultPtr1; resultPtr1 = resultPtr2; resultPtr2 = tempPtr; } // Retrieve real output length outputLength = DigitHelper.GetRealDigitsLength(resultArray2, outputLength); // Create output array outputArray = new uint[outputLength]; // Copy each digit but only if length is not null fixed (uint* outputPtr = outputArray) { for (uint i = 0; i < outputLength; ++i) { if (resultPtr2[i] != 0) { outputPtr[i] = resultPtr1[i]; } } } } // Return temporary arrays to pool ArrayPool.Instance.AddArray(resultArray); ArrayPool.Instance.AddArray(resultArray2); return outputArray; } #endregion #region ClassicToString /// /// Converts digits from internal representaion into given base. /// /// Big integer digits. /// Big integer length. /// Base to use for output. /// Calculated output length (will be corrected inside). /// Conversion result (later will be transformed to string). private static uint[] ClassicToString(uint[] digits, uint length, uint numberBase, ref uint outputLength) { uint[] outputArray = BaseToString(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; } #endregion #region Pow2ToString /// /// Converts digits from internal representation into given base. /// /// Big integer digits. /// Big integer length. /// Base to use for output. /// Calculated output length (will be corrected inside). /// Conversion result (later will be transformed to string). private static uint[] Pow2ToString(uint[] digits, uint length, uint numberBase, ref uint outputLength) { // Calculate real output length int bitsInChar = Bits.Msb(numberBase); ulong digitsBitLength = (ulong)(length - 1) * Constants.DigitBitCount + (ulong)Bits.Msb(digits[length - 1]) + 1UL; uint realOutputLength = (uint)(digitsBitLength / (ulong)bitsInChar); if (digitsBitLength % (ulong)bitsInChar != 0) { ++realOutputLength; } // Prepare shift variables int nextDigitShift = Constants.DigitBitCount - bitsInChar; // after this shift next digit must be used int initialShift = 0; // We will also need bitmask for copying digits uint digitBitMask = numberBase - 1; // Create an output array for storing of number in other base uint[] outputArray = new uint[realOutputLength]; // Walk thru original digits and fill output uint outputDigit; for (uint outputIndex = 0, digitIndex = 0; outputIndex < realOutputLength; ++outputIndex) { // Get part of current digit outputDigit = digits[digitIndex] >> initialShift; // Maybe we need to go to the next digit if (initialShift >= nextDigitShift) { // Go to the next digit ++digitIndex; // Maybe we also need a part of the next digit if (initialShift != nextDigitShift && digitIndex < length) { outputDigit |= digits[digitIndex] << (Constants.DigitBitCount - initialShift); } // Modify shift so that it will be valid for the next digit initialShift = (initialShift + bitsInChar) % Constants.DigitBitCount; } else { // Modify shift as usual initialShift += bitsInChar; } // Write masked result to the output outputArray[outputIndex] = outputDigit & digitBitMask; } outputLength = realOutputLength; return outputArray; } #endregion } #endregion #region Bits /// /// Contains helping methods to with with bits in dword (). /// internal static class Bits { /// /// Returns number of leading zero bits in int. /// /// Int value. /// Number of leading zero bits. static public int Nlz(uint x) { if (x == 0) return 32; int n = 1; if ((x >> 16) == 0) { n += 16; x <<= 16; } if ((x >> 24) == 0) { n += 8; x <<= 8; } if ((x >> 28) == 0) { n += 4; x <<= 4; } if ((x >> 30) == 0) { n += 2; x <<= 2; } return n - (int)(x >> 31); } /// /// Counts position of the most significant bit in int. /// Can also be used as Floor(Log2()). /// /// Int value. /// Position of the most significant one bit (-1 if all zeroes). static public int Msb(uint x) { return 31 - Nlz(x); } /// /// Ceil(Log2()). /// /// Int value. /// Ceil of the Log2. static public int CeilLog2(uint x) { int msb = Msb(x); if (x != 1U << msb) { ++msb; } return msb; } } #endregion #region DivModResultFlags /// /// divide results to return. /// [Flags] internal enum DivModResultFlags { /// /// Dividend is returned. /// Div = 1, /// /// Remainder is returned. /// Mod = 2 } #endregion #region Constants /// /// Constants used in and helping classes. /// static internal class Constants { #region .cctor static Constants() { BaseCharToDigits = StrRepHelper.CharDictionaryFromAlphabet(new string(BaseUpperChars), 16U); for (int i = 10; i < BaseLowerChars.Length; i++) { BaseCharToDigits.Add(BaseLowerChars[i], (uint)i); } } #endregion .cctor #region ToString constants /// /// Chars used to parse/output big integers (upper case). /// public static readonly char[] BaseUpperChars = "0123456789ABCDEF".ToCharArray(); /// /// Chars used to parse/output big integers (lower case). /// public static readonly char[] BaseLowerChars = "0123456789abcdef".ToCharArray(); /// /// Standard char->digit dictionary. /// static readonly public IDictionary BaseCharToDigits; /// /// Digit opening bracket (used for bases bigger then 16). /// public const char DigitOpeningBracet = '{'; /// /// Digit closing bracket (used for bases bigger then 16). /// public const char DigitClosingBracet = '}'; /// /// Minus char (-). /// public const char DigitsMinusChar = '-'; /// /// Natural logarithm of digits base (log(2^32)). /// public static readonly double DigitBaseLog = System.Math.Log(1UL << DigitBitCount); #endregion ToString constants #region Pools constants /// /// Minimal Log2(array length) which will be pooled using any array pool. /// public const int MinPooledArraySizeLog2 = 17; /// /// Maximal Log2(array length) which will be pooled using any array pool. /// public const int MaxPooledArraySizeLog2 = 31; /// /// Maximal allowed array pool items count in each stack. /// public const int MaxArrayPoolCount = 1024; #endregion Pools constants #region FHT constants /// /// length from which FHT is used (in auto-FHT mode). /// Before this length usual multiply algorithm works faster. /// public const uint AutoFhtLengthLowerBound = 1U << 9; /// /// length 'till which FHT is used (in auto-FHT mode). /// After this length using of FHT may be unsafe due to big precision errors. /// public const uint AutoFhtLengthUpperBound = 1U << 26; /// /// Number of lower digits used to check FHT multiplication result validity. /// public const uint FhtValidityCheckDigitCount = 10; #endregion FHT constants #region Newton constants /// /// length from which Newton approach is used (in auto-Newton mode). /// Before this length usual divide algorithm works faster. /// public const uint AutoNewtonLengthLowerBound = 1U << 13; /// /// length 'till which Newton approach is used (in auto-Newton mode). /// After this length using of fast division may be slow. /// public const uint AutoNewtonLengthUpperBound = 1U << 26; #endregion Newton constants #region Parsing constants /// /// length from which fast parsing is used (in Fast parsing mode). /// Before this length usual parsing algorithm works faster. /// public const uint FastParseLengthLowerBound = 32; /// /// length 'till which fast parsing is used (in Fast parsing mode). /// After this length using of parsing will be slow. /// public const uint FastParseLengthUpperBound = uint.MaxValue; #endregion Parsing constants #region ToString convertion constants /// /// length from which fast conversion is used (in Fast convert mode). /// Before this length usual conversion algorithm works faster. /// public const uint FastConvertLengthLowerBound = 16; /// /// length 'till which fast conversion is used (in Fast convert mode). /// After this length using of conversion will be slow. /// public const uint FastConvertLengthUpperBound = uint.MaxValue; #endregion ToString convertion constants /// /// Count of bits in one digit. /// public const int DigitBitCount = 32; /// /// Maximum count of bits which can fit in . /// public const ulong MaxBitCount = uint.MaxValue * 32UL; /// /// 2^. /// public const ulong BitCountStepOf2 = 1UL << 32; } #endregion #region ArrayPool /// /// Generic arrays pool on weak references. /// sealed internal class ArrayPool { #region Fields /// /// Singleton instance. /// static readonly public ArrayPool Instance = new ArrayPool( Constants.MinPooledArraySizeLog2, Constants.MaxPooledArraySizeLog2, Constants.MaxArrayPoolCount); // Minimal and maximal Log2(uint[] length) which will be pooled int _minPooledArraySizeLog2; int _maxPooledArraySizeLog2; // Maximal pool items count int _maxPoolCount; Stack[] _pools; // ArrayPool pools #endregion Fields #region Constructor // Singleton private ArrayPool(int minPooledArraySizeLog2, int maxPooledArraySizeLog2, int maxPoolCount) { _minPooledArraySizeLog2 = minPooledArraySizeLog2; _maxPooledArraySizeLog2 = maxPooledArraySizeLog2; _maxPoolCount = maxPoolCount; // Init pools _pools = new Stack[maxPooledArraySizeLog2 - minPooledArraySizeLog2 + 1]; for (int i = 0; i < _pools.Length; ++i) { // Calls to those pools are sync in code _pools[i] = new Stack(); } } #endregion Constructor #region Pool management methods /// /// Either returns array of given size from pool or creates it. /// /// Array length (always pow of 2). /// Always array instance ready to use. public T[] GetArray(uint length) { int lengthLog2 = Bits.Msb(length); // Check if we can search in pool if (lengthLog2 >= _minPooledArraySizeLog2 && lengthLog2 <= _maxPooledArraySizeLog2) { // Get needed pool Stack pool = _pools[lengthLog2 - _minPooledArraySizeLog2]; // Try to find at least one not collected array of given size while (pool.Count > 0) { WeakReference arrayRef; lock (pool) { // Double-guard here if (pool.Count > 0) { arrayRef = pool.Pop(); } else { // Well, we can exit here break; } } // Maybe return found array if link is alive T[] array = (T[])arrayRef.Target; if (arrayRef.IsAlive) return array; } } // Array can't be found in pool - create new one return new T[length]; } /// /// Adds array to pool. /// /// Array to add (it/s length is always pow of 2). public void AddArray(T[] array) { int lengthLog2 = Bits.Msb((uint)array.LongLength); // Check if we can add in pool if (lengthLog2 >= _minPooledArraySizeLog2 && lengthLog2 <= _maxPooledArraySizeLog2) { // Get needed pool Stack pool = _pools[lengthLog2 - _minPooledArraySizeLog2]; // Add array to pool (only if pool size is not too big) if (pool.Count <= _maxPoolCount) { lock (pool) { // Double-guard here if (pool.Count <= _maxPoolCount) { pool.Push(new WeakReference(array)); } } } } } #endregion Pool management methods } #endregion #region StrRepHelper /// /// Helps to work with string representations. /// static internal class StrRepHelper { /// /// Returns digit for given char. /// /// Char->digit dictionary. /// Char which represents big integer digit. /// String representation number base. /// Digit. /// is not in valid format. static public uint GetDigit(IDictionary charToDigits, char ch, uint numberBase) { if (charToDigits == null) { throw new ArgumentNullException("charToDigits"); } // Try to identify this digit uint digit; if (!charToDigits.TryGetValue(ch, out digit)) { throw new FormatException("Invalid character in input."); } if (digit >= numberBase) { throw new FormatException("Digit is too big for this base."); } return digit; } /// /// Verfies string alphabet provider by user for validity. /// /// Alphabet. /// String representation number base. static public void AssertAlphabet(string alphabet, uint numberBase) { if (alphabet == null) { throw new ArgumentNullException("alphabet"); } // Ensure that alphabet has enough characters to represent numbers in given base if (alphabet.Length < numberBase) { throw new ArgumentException(string.Format("Alphabet is too small to represent numbers in base {0}.", numberBase), "alphabet"); } // Ensure that all the characters in alphabet are unique char[] sortedChars = alphabet.ToCharArray(); Array.Sort(sortedChars); for (int i = 0; i < sortedChars.Length; i++) { if (i > 0 && sortedChars[i] == sortedChars[i - 1]) { throw new ArgumentException("Alphabet characters must be unique.", "alphabet"); } } } /// /// Generates char->digit dictionary from alphabet. /// /// Alphabet. /// String representation number base. /// Char->digit dictionary. static public IDictionary CharDictionaryFromAlphabet(string alphabet, uint numberBase) { AssertAlphabet(alphabet, numberBase); Dictionary charToDigits = new Dictionary((int)numberBase); for (int i = 0; i < numberBase; i++) { charToDigits.Add(alphabet[i], (uint)i); } return charToDigits; } } #endregion #region OpHelper /// /// Contains helping methods for operations over . /// static internal class OpHelper { #region Add operation /// /// Adds two big integers. /// /// First big integer. /// Second big integer. /// Resulting big integer. /// or is too big for add operation. 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("One of the operated big integers is too big."); } // 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; } #endregion Add operation #region Subtract operation /// /// Subtracts two big integers. /// /// First big integer. /// Second big integer. /// Resulting big integer. 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; } #endregion Subtract operation #region Add/Subtract operation - common methods /// /// Adds/subtracts one to/from another. /// Determines which operation to use basing on operands signs. /// /// First big integer. /// Second big integer. /// Was subtraction initially. /// Add/subtract operation result. /// or is a null reference. static public IntX AddSub(IntX int1, IntX int2, bool subtract) { // Exceptions if (ReferenceEquals(int1, null)) { throw new ArgumentNullException("int1", "Operands can't be null."); } else if (ReferenceEquals(int2, null)) { throw new ArgumentNullException("int2", "Operands can't be null."); } // Determine real operation type and result sign return subtract ^ int1._negative == int2._negative ? Add(int1, int2) : Sub(int1, int2); } #endregion Add/Subtract operation - common methods #region Power operation /// /// Returns a specified big integer raised to the specified power. /// /// Number to raise. /// Power. /// Number in given power. /// is a null reference. static public IntX Pow(IntX value, uint power) { // Exception if (ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } // Return one for zero pow if (power == 0) return 1; // Return the number itself from a power of one if (power == 1) return new IntX(value); // Return zero for a zero if (value._length == 0) return new IntX(); // Get first one bit int msb = Bits.Msb(power); // Do actual raising IntX res = value; for (uint powerMask = 1U << (msb - 1); powerMask != 0; powerMask >>= 1) { // Always square res = IntXMultiplier.Multiply(res, res); // Maybe mul if ((power & powerMask) != 0) { res = IntXMultiplier.Multiply(res, value); } } return res; } #endregion Power operation #region Compare operation /// /// Compares 2 objects. /// Returns "-2" if any argument is null, "-1" if < , /// "0" if equal and "1" if >. /// /// First big integer. /// Second big integer. /// Raises or not . /// Comparison result. /// or is a null reference and is set to true. 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", "Can't use null in comparsion operations."); } 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); } /// /// Compares object to int. /// Returns "-1" if < , "0" if equal and "1" if >. /// /// First big integer. /// Second integer. /// Comparison result. static public int Cmp(IntX int1, int int2) { // Special processing for zero if (int2 == 0) return int1._length == 0 ? 0 : (int1._negative ? -1 : 1); if (int1._length == 0) return int2 > 0 ? -1 : 1; // Compare presentation if (int1._length > 1) return int1._negative ? -1 : 1; uint digit2; bool negative2; DigitHelper.ToUInt32WithSign(int2, out digit2, out negative2); // Compare sign if (int1._negative && !negative2) return -1; if (!int1._negative && negative2) return 1; return int1._digits[0] == digit2 ? 0 : (int1._digits[0] < digit2 ^ negative2 ? -1 : 1); } /// /// Compares object to unsigned int. /// Returns "-1" if < , "0" if equal and "1" if >. /// For internal use. /// /// First big integer. /// Second unsigned integer. /// Comparsion result. static public int Cmp(IntX int1, uint int2) { // Special processing for zero if (int2 == 0) return int1._length == 0 ? 0 : (int1._negative ? -1 : 1); if (int1._length == 0) return -1; // Compare presentation if (int1._negative) return -1; if (int1._length > 1) return 1; return int1._digits[0] == int2 ? 0 : (int1._digits[0] < int2 ? -1 : 1); } #endregion Compare operation #region Shift operation /// /// Shifts object. /// Determines which operation to use basing on shift sign. /// /// Big integer. /// Bits count to shift. /// If true the shifting to the left. /// Bitwise shift operation result. /// is a null reference. static public IntX Sh(IntX intX, long shift, bool toLeft) { // Exceptions if (ReferenceEquals(intX, null)) { throw new ArgumentNullException("intX", "Operand can't be null."); } // 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) 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("One of the operated big integers is too big.", "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); } 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); } } return newInt; } #endregion Shift operation } #endregion #region NewtonHelper /// /// Contains helping methods for fast division /// using Newton approximation approach and fast multiplication. /// static internal class NewtonHelper { /// /// Generates integer opposite to the given one using approximation. /// Uses algorithm from Khuth vol. 2 3rd Edition (4.3.3). /// /// Initial big integer digits. /// Initial big integer length. /// Precision length. /// Buffer in which shifted big integer may be stored. /// Resulting big integer length. /// How much resulting big integer is shifted to the left (or: must be shifted to the right). /// Resulting big integer digits. 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.Instance.GetArray(newLengthMax); //new uint[newLengthMax]; uint resultLength; // Create temporary digits for squared result (twice more size) uint[] resultDigitsSqr = ArrayPool.Instance.GetArray(newLengthMax); //new uint[newLengthMax]; uint resultLengthSqr; // Create temporary digits for squared result * buffer uint[] resultDigitsSqrBuf = new uint[newLengthMax + length]; uint resultLengthSqrBuf; // 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 = IntXMultiplier.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 = IntXMultiplier.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.Instance.AddArray(resultDigitsSqr); rightShift += (1UL << lengthLog2Bits) + 1UL; newLength = resultLength; return resultDigits; } } #endregion #region FhtHelper /// /// Contains helping methods for work with FHT (Fast Hartley Transform). /// FHT is a better alternative of FFT (Fast Fourier Transform) - at least for . /// static unsafe internal class FhtHelper { #region struct TrigValues /// /// Trigonometry values. /// internal struct TrigValues { /// /// Sin value from . /// public double TableSin; /// /// Cos value from . /// public double TableCos; /// /// Sin value. /// public double Sin; /// /// Cos value. /// public double Cos; } #endregion struct SinCos #region Private constants (or static readonly fields) // double[] data base const int DoubleDataBytes = 1; const int DoubleDataLengthShift = 2 - (DoubleDataBytes >> 1); const int DoubleDataDigitShift = DoubleDataBytes << 3; const long DoubleDataBaseInt = 1L << DoubleDataDigitShift; const double DoubleDataBase = DoubleDataBaseInt; const double DoubleDataBaseDiv2 = DoubleDataBase / 2.0; // SQRT(2) and SQRT(2) / 2 static readonly double Sqrt2 = System.Math.Sqrt(2.0); static readonly double Sqrt2Div2 = Sqrt2 / 2.0; // SIN() table static readonly double[] SineTable = new double[31]; #endregion Private constants (or static readonly fields) #region Constructors // .cctor static FhtHelper() { // Initialize SinTable FillSineTable(SineTable); } #endregion Constructors #region Data conversion methods /// /// Converts digits into real representation (used in FHT). /// /// Big integer digits. /// length. /// Multiplication result length (must be pow of 2). /// Double array. static public double[] ConvertDigitsToDouble(uint[] digits, uint length, uint newLength) { fixed (uint* digitsPtr = digits) { return ConvertDigitsToDouble(digitsPtr, length, newLength); } } /// /// Converts digits into real representation (used in FHT). /// /// Big integer digits. /// length. /// Multiplication result length (must be pow of 2). /// Double array. static public double[] ConvertDigitsToDouble(uint* digitsPtr, uint length, uint newLength) { // Maybe fix newLength (make it the nearest bigger pow of 2) newLength = 1U << Bits.CeilLog2(newLength); // For better FHT accuracy we will choose length smaller then dwords. // So new length must be modified accordingly newLength <<= DoubleDataLengthShift; double[] data = ArrayPool.Instance.GetArray(newLength); // Run in unsafe context fixed (double* slice = data) { // Amount of units pointed by digitsPtr uint unitCount = length << DoubleDataLengthShift; // Copy all words from digits into new double[] byte* unitDigitsPtr = (byte*)digitsPtr; for (uint i = 0; i < unitCount; ++i) { slice[i] = unitDigitsPtr[i]; } // Clear remaining double values (this array is from pool and may be dirty) DigitHelper.SetBlockDigits(slice + unitCount, newLength - unitCount, 0.0); // FHT (as well as FFT) works more accurate with "balanced" data, so let's balance it double carry = 0, dataDigit; for (uint i = 0; i < unitCount || i < newLength && carry != 0; ++i) { dataDigit = slice[i] + carry; if (dataDigit >= DoubleDataBaseDiv2) { dataDigit -= DoubleDataBase; carry = 1.0; } else { carry = 0; } slice[i] = dataDigit; } if (carry > 0) { slice[0] -= carry; } } return data; } /// /// Converts real digits representation (result of FHT) into usual digits. /// /// Real digits representation. /// length. /// New digits array length (we always do know the upper value for this array). /// Big integer digits. static unsafe public void ConvertDoubleToDigits(double[] array, uint length, uint digitsLength, uint[] digitsRes) { fixed (double* slice = array) fixed (uint* digitsResPtr = digitsRes) { ConvertDoubleToDigits(slice, length, digitsLength, digitsResPtr); } } /// /// Converts real digits representation (result of FHT) into usual digits. /// /// Real digits representation. /// length. /// New digits array length (we always do know the upper value for this array). /// Resulting digits storage. /// Big integer digits (dword values). static unsafe public void ConvertDoubleToDigits(double* slice, uint length, uint digitsLength, uint* digitsResPtr) { // Calculate data multiplier (don't forget about additional div 2) double normalizeMultiplier = 0.5 / length; // Count of units in digits uint unitCount = digitsLength << DoubleDataLengthShift; // Carry and current digit double carry = 0, dataDigit; long carryInt = 0, dataDigitInt; // Walk thru all double digits byte* unitDigitsPtr = (byte*)digitsResPtr; for (uint i = 0; i < length; ++i) { // Get data digit (don't forget it might be balanced) dataDigit = slice[i] * normalizeMultiplier; // Round to the nearest dataDigitInt = (long)(dataDigit < 0 ? dataDigit - 0.5 : dataDigit + 0.5) + carryInt; // Get next carry floored; maybe modify data digit carry = dataDigitInt / DoubleDataBase; if (carry < 0) { carry += carry % 1.0; } carryInt = (long)carry; dataDigitInt -= carryInt << DoubleDataDigitShift; if (dataDigitInt < 0) { dataDigitInt += DoubleDataBaseInt; --carryInt; } // Maybe add to the digits if (i < unitCount) { unitDigitsPtr[i] = (byte)dataDigitInt; } } // Last carry must be accounted if (carryInt < 0) { digitsResPtr[0] -= (uint)-carryInt; } else if (carryInt > 0) { uint digitsCarry = (uint)carryInt, oldDigit; for (uint i = 0; digitsCarry != 0 && i < digitsLength; ++i) { oldDigit = digitsResPtr[i]; digitsResPtr[i] += digitsCarry; // Check for an overflow digitsCarry = digitsResPtr[i] < oldDigit ? 1U : 0U; } } } #endregion Data conversion methods #region FHT /// /// Performs FHT "in place" for given double[] array. /// /// Double array. /// Array length. static public void Fht(double[] array, uint length) { fixed (double* slice = array) { Fht(slice, length, Bits.Msb(length)); } } /// /// Performs FHT "in place" for given double[] array slice. /// /// Double array slice. /// Slice length. /// Log2(). static public void Fht(double* slice, uint length, int lengthLog2) { // Special fast processing for length == 4 if (length == 4) { Fht4(slice); return; } // Divide data into 2 recursively processed parts length >>= 1; --lengthLog2; double* rightSlice = slice + length; uint lengthDiv2 = length >> 1; uint lengthDiv4 = length >> 2; // Perform initial "butterfly" operations over left and right array parts double leftDigit = slice[0]; double rightDigit = rightSlice[0]; slice[0] = leftDigit + rightDigit; rightSlice[0] = leftDigit - rightDigit; leftDigit = slice[lengthDiv2]; rightDigit = rightSlice[lengthDiv2]; slice[lengthDiv2] = leftDigit + rightDigit; rightSlice[lengthDiv2] = leftDigit - rightDigit; // Get initial trig values //TrigValues trigValues = GetInitialTrigValues(lengthLog2); TrigValues trigValues = new TrigValues(); GetInitialTrigValues(&trigValues, lengthLog2); // Perform "butterfly" for (uint i = 1; i < lengthDiv4; ++i) { FhtButterfly(slice, rightSlice, i, length - i, trigValues.Cos, trigValues.Sin); FhtButterfly(slice, rightSlice, lengthDiv2 - i, lengthDiv2 + i, trigValues.Sin, trigValues.Cos); // Get next trig values NextTrigValues(&trigValues); } // Final "butterfly" FhtButterfly(slice, rightSlice, lengthDiv4, length - lengthDiv4, Sqrt2Div2, Sqrt2Div2); // Finally perform recursive run Fht(slice, length, lengthLog2); Fht(rightSlice, length, lengthLog2); } /// /// Performs FHT "in place" for given double[] array slice. /// Fast version for length == 4. /// /// Double array slice. static private void Fht4(double* slice) { // Get 4 digits double d0 = slice[0]; double d1 = slice[1]; double d2 = slice[2]; double d3 = slice[3]; // Perform fast "butterfly" addition/subtraction for them. // In case when length == 4 we can do it without trigonometry double d02 = d0 + d2; double d13 = d1 + d3; slice[0] = d02 + d13; slice[1] = d02 - d13; d02 = d0 - d2; d13 = d1 - d3; slice[2] = d02 + d13; slice[3] = d02 - d13; } #endregion FHT #region FHT results multiplication /// /// Multiplies two FHT results and stores multiplication in first one. /// /// First FHT result. /// Second FHT result. /// FHT results length. static public void MultiplyFhtResults(double[] data, double[] data2, uint length) { fixed (double* slice = data, slice2 = data2) { MultiplyFhtResults(slice, slice2, length); } } /// /// Multiplies two FHT results and stores multiplication in first one. /// /// First FHT result. /// Second FHT result. /// FHT results length. static public void MultiplyFhtResults(double* slice, double* slice2, uint length) { // Step0 and Step1 slice[0] *= 2.0 * slice2[0]; slice[1] *= 2.0 * slice2[1]; // Perform all other steps double d11, d12, d21, d22, ad, sd; for (uint stepStart = 2, stepEnd = 4, index1, index2; stepStart < length; stepStart *= 2, stepEnd *= 2) { for (index1 = stepStart, index2 = stepEnd - 1; index1 < stepEnd; index1 += 2, index2 -= 2) { d11 = slice[index1]; d12 = slice[index2]; d21 = slice2[index1]; d22 = slice2[index2]; ad = d11 + d12; sd = d11 - d12; slice[index1] = d21 * ad + d22 * sd; slice[index2] = d22 * ad - d21 * sd; } } } #endregion FHT results multiplication #region Reverse FHT /// /// Performs FHT reverse "in place" for given double[] array. /// /// Double array. /// Array length. static public void ReverseFht(double[] array, uint length) { fixed (double* slice = array) { ReverseFht(slice, length, Bits.Msb(length)); } } /// /// Performs reverse FHT "in place" for given double[] array slice. /// /// Double array slice. /// Slice length. /// Log2(). static public void ReverseFht(double* slice, uint length, int lengthLog2) { // Special fast processing for length == 8 if (length == 8) { ReverseFht8(slice); return; } // Divide data into 2 recursively processed parts length >>= 1; --lengthLog2; double* rightSlice = slice + length; uint lengthDiv2 = length >> 1; uint lengthDiv4 = length >> 2; // Perform recursive run ReverseFht(slice, length, lengthLog2); ReverseFht(rightSlice, length, lengthLog2); // Get initial trig values TrigValues trigValues = new TrigValues(); GetInitialTrigValues(&trigValues, lengthLog2); // Perform "butterfly" for (uint i = 1; i < lengthDiv4; ++i) { ReverseFhtButterfly(slice, rightSlice, i, length - i, trigValues.Cos, trigValues.Sin); ReverseFhtButterfly(slice, rightSlice, lengthDiv2 - i, lengthDiv2 + i, trigValues.Sin, trigValues.Cos); // Get next trig values NextTrigValues(&trigValues); } // Final "butterfly" ReverseFhtButterfly(slice, rightSlice, lengthDiv4, length - lengthDiv4, Sqrt2Div2, Sqrt2Div2); ReverseFhtButterfly2(slice, rightSlice, 0, 0, 1.0, 0); ReverseFhtButterfly2(slice, rightSlice, lengthDiv2, lengthDiv2, 0, 1.0); } /// /// Performs reverse FHT "in place" for given double[] array slice. /// Fast version for length == 8. /// /// Double array slice. static private void ReverseFht8(double* slice) { // Get 8 digits double d0 = slice[0]; double d1 = slice[1]; double d2 = slice[2]; double d3 = slice[3]; double d4 = slice[4]; double d5 = slice[5]; double d6 = slice[6]; double d7 = slice[7]; // Calculate add and subtract pairs for first 4 digits double da01 = d0 + d1; double ds01 = d0 - d1; double da23 = d2 + d3; double ds23 = d2 - d3; // Calculate add and subtract pairs for first pairs double daa0123 = da01 + da23; double dsa0123 = da01 - da23; double das0123 = ds01 + ds23; double dss0123 = ds01 - ds23; // Calculate add and subtract pairs for next 4 digits double da45 = d4 + d5; double ds45 = (d4 - d5) * Sqrt2; double da67 = d6 + d7; double ds67 = (d6 - d7) * Sqrt2; // Calculate add and subtract pairs for next pairs double daa4567 = da45 + da67; double dsa4567 = da45 - da67; // Store digits values slice[0] = daa0123 + daa4567; slice[4] = daa0123 - daa4567; slice[2] = dsa0123 + dsa4567; slice[6] = dsa0123 - dsa4567; slice[1] = das0123 + ds45; slice[5] = das0123 - ds45; slice[3] = dss0123 + ds67; slice[7] = dss0123 - ds67; } #endregion Reverse FHT #region "Butterfly" methods for FHT /// /// Performs "butterfly" operation for . /// /// First data array slice. /// Second data array slice. /// First slice index. /// Second slice index. /// Cos value. /// Sin value. static private void FhtButterfly(double* slice1, double* slice2, uint index1, uint index2, double cos, double sin) { double d11 = slice1[index1]; double d12 = slice1[index2]; double temp = slice2[index1]; slice1[index1] = d11 + temp; d11 -= temp; temp = slice2[index2]; slice1[index2] = d12 + temp; d12 -= temp; slice2[index1] = d11 * cos + d12 * sin; slice2[index2] = d11 * sin - d12 * cos; } /// /// Performs "butterfly" operation for . /// /// First data array slice. /// Second data array slice. /// First slice index. /// Second slice index. /// Cos value. /// Sin value. static private void ReverseFhtButterfly(double* slice1, double* slice2, uint index1, uint index2, double cos, double sin) { double d21 = slice2[index1]; double d22 = slice2[index2]; double temp = slice1[index1]; double temp2 = d21 * cos + d22 * sin; slice1[index1] = temp + temp2; slice2[index1] = temp - temp2; temp = slice1[index2]; temp2 = d21 * sin - d22 * cos; slice1[index2] = temp + temp2; slice2[index2] = temp - temp2; } /// /// Performs "butterfly" operation for . /// Another version. /// /// First data array slice. /// Second data array slice. /// First slice index. /// Second slice index. /// Cos value. /// Sin value. static private void ReverseFhtButterfly2(double* slice1, double* slice2, uint index1, uint index2, double cos, double sin) { double temp = slice1[index1]; double temp2 = slice2[index1] * cos + slice2[index2] * sin; slice1[index1] = temp + temp2; slice2[index2] = temp - temp2; } #endregion "Butterfly" methods for FHT #region Trigonometry working methods /// /// Fills sine table for FHT. /// /// Sine table to fill. static private void FillSineTable(double[] sineTable) { for (int i = 0, p = 1; i < sineTable.Length; ++i, p *= 2) { sineTable[i] = System.Math.Sin(System.Math.PI / p); } } /// /// Initializes trigonometry values for FHT. /// /// Values to init. /// Log2(processing slice length). static private void GetInitialTrigValues(TrigValues* valuesPtr, int lengthLog2) { valuesPtr->TableSin = SineTable[lengthLog2]; valuesPtr->TableCos = SineTable[lengthLog2 + 1]; valuesPtr->TableCos *= -2.0 * valuesPtr->TableCos; valuesPtr->Sin = valuesPtr->TableSin; valuesPtr->Cos = valuesPtr->TableCos + 1.0; } /// /// Generates next trigonometry values for FHT basing on previous ones. /// /// Current trig values. static private void NextTrigValues(TrigValues* valuesPtr) { double oldCos = valuesPtr->Cos; valuesPtr->Cos = valuesPtr->Cos * valuesPtr->TableCos - valuesPtr->Sin * valuesPtr->TableSin + valuesPtr->Cos; valuesPtr->Sin = valuesPtr->Sin * valuesPtr->TableCos + oldCos * valuesPtr->TableSin + valuesPtr->Sin; } #endregion Trigonometry working methods } #endregion #region DigitOpHelper /// /// Contains helping methods for operations over digits as arrays. /// static internal class DigitOpHelper { #region Add operation /// /// Adds two big integers. /// /// First big integer digits. /// First big integer length. /// Second big integer digits. /// Second big integer length. /// Resulting big integer digits. /// Resulting big integer length. static unsafe public uint Add(uint[] digits1, uint length1, uint[] digits2, uint length2, uint[] digitsRes) { fixed (uint* digitsPtr1 = digits1, digitsPtr2 = digits2, digitsResPtr = digitsRes) { return Add(digitsPtr1, length1, digitsPtr2, length2, digitsResPtr); } } /// /// Adds two big integers using pointers. /// /// First big integer digits. /// First big integer length. /// Second big integer digits. /// Second big integer length. /// Resulting big integer digits. /// Resulting big integer length. static unsafe public uint Add(uint* digitsPtr1, uint length1, uint* digitsPtr2, uint length2, uint* digitsResPtr) { ulong c = 0; if (length1 < length2) { // First must be bigger - swap uint lengthTemp = length1; length1 = length2; length2 = lengthTemp; uint* ptrTemp = digitsPtr1; digitsPtr1 = digitsPtr2; digitsPtr2 = ptrTemp; } // Perform digits adding for (uint i = 0; i < length2; ++i) { c += (ulong)digitsPtr1[i] + digitsPtr2[i]; digitsResPtr[i] = (uint)c; c >>= 32; } // Perform digits + carry moving for (uint i = length2; i < length1; ++i) { c += digitsPtr1[i]; digitsResPtr[i] = (uint)c; c >>= 32; } // Account last carry if (c != 0) { digitsResPtr[length1++] = (uint)c; } return length1; } #endregion Add operation #region Subtract operation /// /// Subtracts two big integers. /// /// First big integer digits. /// First big integer length. /// Second big integer digits. /// Second big integer length. /// Resulting big integer digits. /// Resulting big integer length. static unsafe public uint Sub(uint[] digits1, uint length1, uint[] digits2, uint length2, uint[] digitsRes) { fixed (uint* digitsPtr1 = digits1, digitsPtr2 = digits2, digitsResPtr = digitsRes) { return Sub(digitsPtr1, length1, digitsPtr2, length2, digitsResPtr); } } /// /// Subtracts two big integers using pointers. /// /// First big integer digits. /// First big integer length. /// Second big integer digits. /// Second big integer length. /// Resulting big integer digits. /// Resulting big integer length. static unsafe public uint Sub(uint* digitsPtr1, uint length1, uint* digitsPtr2, uint length2, uint* digitsResPtr) { ulong c = 0; // Perform digits subtraction for (uint i = 0; i < length2; ++i) { c = (ulong)digitsPtr1[i] - digitsPtr2[i] - c; digitsResPtr[i] = (uint)c; c >>= 63; } // Perform digits + carry moving for (uint i = length2; i < length1; ++i) { c = digitsPtr1[i] - c; digitsResPtr[i] = (uint)c; c >>= 63; } return DigitHelper.GetRealDigitsLength(digitsResPtr, length1); } #endregion Subtract operation #region Divide/modulo operation - when second length == 1 /// /// Divides one big integer represented by it's digits on another one big integer. /// Reminder is always filled (but not the result). /// /// First big integer digits. /// First big integer length. /// Second integer. /// Div result (can be null - not filled in this case). /// Remainder (always filled). /// Result length (0 if result is null). static unsafe public uint DivMod(uint[] digits1, uint length1, uint int2, uint[] divRes, out uint modRes) { fixed (uint* digits1Ptr = digits1, divResPtr = divRes) { return DivMod(digits1Ptr, length1, int2, divResPtr, out modRes); } } /// /// Divides one big integer represented by it's digits on another one big integer. /// Reminder is always filled (but not the result). /// /// First big integer digits. /// First big integer length. /// Second integer. /// Div result (can be null - not filled in this case). /// Remainder (always filled). /// Result length (0 if result is null). static unsafe public uint DivMod(uint* digitsPtr1, uint length1, uint int2, uint* divResPtr, out uint modRes) { ulong c = 0; uint res; for (uint index = length1 - 1; index < length1; --index) { c = (c << Constants.DigitBitCount) + digitsPtr1[index]; res = (uint)(c / int2); c -= (ulong)res * int2; divResPtr[index] = res; } modRes = (uint)c; return length1 - (divResPtr[length1 - 1] == 0 ? 1U : 0U); } /// /// Divides one big integer represented by it's digits on another one big integer. /// Only remainder is filled. /// /// First big integer digits. /// First big integer length. /// Second integer. /// Remainder. static unsafe public uint Mod(uint[] digits1, uint length1, uint int2) { fixed (uint* digitsPtr1 = digits1) { return Mod(digitsPtr1, length1, int2); } } /// /// Divides one big integer represented by it's digits on another one big integer. /// Only remainder is filled. /// /// First big integer digits. /// First big integer length. /// Second integer. /// Remainder. static unsafe public uint Mod(uint* digitsPtr1, uint length1, uint int2) { ulong c = 0; uint res; for (uint* ptr1 = digitsPtr1 + length1 - 1; ptr1 >= digitsPtr1; --ptr1) { c = (c << Constants.DigitBitCount) + *ptr1; res = (uint)(c / int2); c -= (ulong)res * int2; } return (uint)c; } #endregion Divide/modulo operation - when second length == 1 #region Compare operation /// /// Compares 2 objects represented by digits only (not taking sign into account). /// Returns "-1" if < , "0" if equal and "1" if >. /// /// First big integer digits. /// First big integer length. /// Second big integer digits. /// Second big integer length. /// Comparison result. static unsafe public int Cmp(uint[] digits1, uint length1, uint[] digits2, uint length2) { // Always compare length if one of the integers has zero length if (length1 == 0 || length2 == 0) return CmpLen(length1, length2); fixed (uint* digitsPtr1 = digits1, digitsPtr2 = digits2) { return Cmp(digitsPtr1, length1, digitsPtr2, length2); } } /// /// Compares 2 objects represented by pointers only (not taking sign into account). /// Returns "-1" if < , "0" if equal and "1" if >. /// /// First big integer digits. /// First big integer length. /// Second big integer digits. /// Second big integer length. /// Comparison result. static unsafe public int Cmp(uint* digitsPtr1, uint length1, uint* digitsPtr2, uint length2) { // Maybe length comparing will be enough int res = CmpLen(length1, length2); if (res != -2) return res; for (uint index = length1 - 1; index < length1; --index) { if (digitsPtr1[index] != digitsPtr2[index]) return digitsPtr1[index] < digitsPtr2[index] ? -1 : 1; } return 0; } /// /// Compares two integers lengths. Returns -2 if further comparing is needed. /// /// First big integer length. /// Second big integer length. /// Comparison result. static int CmpLen(uint length1, uint length2) { if (length1 < length2) return -1; if (length1 > length2) return 1; return length1 == 0 ? 0 : -2; } #endregion Compare operation #region Shift operation /// /// Shifts big integer. /// /// Big integer digits. /// Big integer digits offset. /// Big integer length. /// Resulting big integer digits. /// Resulting big integer digits offset. /// Shift to the right (always between 1 an 31). static unsafe public void Shr(uint[] digits, uint offset, uint length, uint[] digitsRes, uint resOffset, int rightShift) { fixed (uint* digitsPtr = digits, digitsResPtr = digitsRes) { Shr(digitsPtr + offset, length, digitsResPtr + resOffset, rightShift, resOffset != 0); } } /// /// Shifts big integer. /// /// Big integer digits. /// Big integer length. /// Resulting big integer digits. /// Shift to the right (always between 1 an 31). /// True if has offset. /// Resulting big integer length. unsafe static public uint Shr(uint* digitsPtr, uint length, uint* digitsResPtr, int rightShift, bool resHasOffset) { int rightShiftRev = Constants.DigitBitCount - rightShift; // Shift first digit in special way if (resHasOffset) { digitsResPtr[-1] = digitsPtr[0] << rightShiftRev; } if (rightShift == 0) { // Handle special situation here - only memcpy is needed (maybe) if (digitsPtr != digitsResPtr) { DigitHelper.DigitsBlockCopy(digitsPtr, digitsResPtr, length); } } else { // Shift all digits except last one uint* digitsPtrEndPrev = digitsPtr + length - 1; uint* digitsPtrNext = digitsPtr + 1; for (; digitsPtr < digitsPtrEndPrev; ++digitsPtr, ++digitsPtrNext, ++digitsResPtr) { *digitsResPtr = *digitsPtr >> rightShift | *digitsPtrNext << rightShiftRev; } // Shift last digit in special way uint lastValue = *digitsPtr >> rightShift; if (lastValue != 0) { *digitsResPtr = lastValue; } else { --length; } } return length; } #endregion Shift operation } #endregion #region DigitHelper /// /// Contains big integer uint[] digits utility methods. /// static internal class DigitHelper { #region Working with digits length methods /// /// Returns real length of digits array (excluding leading zeroes). /// /// Big integer digits. /// Initial big integers length. /// Real length. static public uint GetRealDigitsLength(uint[] digits, uint length) { for (; length > 0 && digits[length - 1] == 0; --length) ; return length; } /// /// Returns real length of digits array (excluding leading zeroes). /// /// Big integer digits. /// Initial big integers length. /// Real length. static unsafe public uint GetRealDigitsLength(uint* digits, uint length) { for (; length > 0 && digits[length - 1] == 0; --length) ; return length; } /// /// Determines object with lower length. /// /// First big integer. /// Second big integer. /// Resulting smaller big integer (by length only). /// Resulting bigger big integer (by length only). static public void GetMinMaxLengthObjects(IntX int1, IntX int2, out IntX smallerInt, out IntX biggerInt) { if (int1._length < int2._length) { smallerInt = int1; biggerInt = int2; } else { smallerInt = int2; biggerInt = int1; } } #endregion Working with digits length methods #region Signed to unsigned+sign conversion methods /// /// Converts int value to uint digit and value sign. /// /// Initial value. /// Resulting unsigned part. /// Resulting sign. static public void ToUInt32WithSign(int value, out uint resultValue, out bool negative) { negative = value < 0; resultValue = !negative ? (uint)value : value != int.MinValue ? (uint)-value : int.MaxValue + 1U; } /// /// Converts long value to ulong digit and value sign. /// /// Initial value. /// Resulting unsigned part. /// Resulting sign. static public void ToUInt64WithSign(long value, out ulong resultValue, out bool negative) { negative = value < 0; resultValue = !negative ? (ulong)value : value != long.MinValue ? (ulong)-value : long.MaxValue + 1UL; } #endregion Signed to unsigned+sign conversion methods #region Working with digits directly methods /// /// Sets digits in given block to given value. /// /// Block start pointer. /// Block length. /// Value to set. static unsafe public void SetBlockDigits(uint* block, uint blockLength, uint value) { for (uint* blockEnd = block + blockLength; block < blockEnd; *block++ = value) ; } /// /// Sets digits in given block to given value. /// /// Block start pointer. /// Block length. /// Value to set. unsafe static public void SetBlockDigits(double* block, uint blockLength, double value) { for (double* blockEnd = block + blockLength; block < blockEnd; *block++ = value) ; } /// /// Copies digits from one block to another. /// /// From block start pointer. /// To block start pointer. /// Count of dwords to copy. static unsafe public void DigitsBlockCopy(uint* blockFrom, uint* blockTo, uint count) { for (uint* blockFromEnd = blockFrom + count; blockFrom < blockFromEnd; *blockTo++ = *blockFrom++) ; } #endregion Working with digits directly methods } #endregion #endregion internal uint[] _digits; // big integer digits internal uint _length; // big integer digits length internal bool _negative; // big integer sign ("-" if true) #region Constructors /// /// Creates new big integer with zero value. /// public IntX() : this(0) {} /// /// Creates new big integer from integer value. /// /// Integer value to create big integer from. public IntX(int value) { if (value == 0) { // Very specific fast processing for zero values InitFromZero(); } else { // Prepare internal fields _digits = new uint[_length = 1]; // Fill the only big integer digit DigitHelper.ToUInt32WithSign(value, out _digits[0], out _negative); } } /// /// Creates new big integer from unsigned integer value. /// /// Unsigned integer value to create big integer from. public IntX(uint value) { if (value == 0) { // Very specific fast processing for zero values InitFromZero(); } else { // Prepare internal fields _digits = new uint[] { value }; _length = 1; } } /// /// Creates new big integer from long value. /// /// Long value to create big integer from. public IntX(long value) { if (value == 0) { // Very specific fast processing for zero values InitFromZero(); } else { // Fill the only big integer digit ulong newValue; DigitHelper.ToUInt64WithSign(value, out newValue, out _negative); InitFromUlong(newValue); } } /// /// Creates new big integer from unsigned long value. /// /// Unsigned long value to create big integer from. public IntX(ulong value) { if (value == 0) { // Very specific fast processing for zero values InitFromZero(); } else { InitFromUlong(value); } } /// /// Creates new big integer from array of it's "digits". /// Digit with lower index has less weight. /// /// Array of digits. /// True if this number is negative. /// is a null reference. public IntX(uint[] digits, bool negative) { // Exceptions if (digits == null) { throw new ArgumentNullException("values"); } InitFromDigits(digits, negative, DigitHelper.GetRealDigitsLength(digits, (uint)digits.LongLength)); } /// /// Creates new from string. /// /// Number as string. public IntX(string value) { IntX intX = Parse(value); InitFromIntX(intX); } /// /// Creates new from string. /// /// Number as string. /// Number base. public IntX(string value, uint numberBase) { IntX intX = Parse(value, numberBase); InitFromIntX(intX); } /// /// Copy constructor. /// /// Value to copy from. /// is a null reference. public IntX(IntX value) { // Exceptions if (value == null) { throw new ArgumentNullException("value"); } InitFromIntX(value); } /// /// Creates new empty big integer with desired sign and length. /// /// For internal use. /// /// Desired digits length. /// Desired integer sign. internal IntX(uint length, bool negative) { _digits = new uint[_length = length]; _negative = negative; } /// /// Creates new big integer from array of it's "digits" but with given length. /// Digit with lower index has less weight. /// /// For internal use. /// /// Array of digits. /// True if this number is negative. /// Length to use for internal digits array. /// is a null reference. internal IntX(uint[] digits, bool negative, uint length) { // Exceptions if (digits == null) { throw new ArgumentNullException("values"); } InitFromDigits(digits, negative, length); } #endregion Constructors #region Operators #region operator== /// /// Compares two objects and returns true if their internal state is equal. /// /// First big integer. /// Second big integer. /// True if equals. static public bool operator ==(IntX int1, IntX int2) { return OpHelper.Cmp(int1, int2, false) == 0; } /// /// Compares object with integer and returns true if their internal state is equal. /// /// First big integer. /// Second integer. /// True if equals. static public bool operator ==(IntX int1, int int2) { return OpHelper.Cmp(int1, int2) == 0; } /// /// Compares integer with object and returns true if their internal state is equal. /// /// First integer. /// Second big integer. /// True if equals. static public bool operator ==(int int1, IntX int2) { return OpHelper.Cmp(int2, int1) == 0; } /// /// Compares object with unsinged integer and returns true if their internal state is equal. /// /// First big integer. /// Second unsinged integer. /// True if equals. static public bool operator ==(IntX int1, uint int2) { return OpHelper.Cmp(int1, int2) == 0; } /// /// Compares unsinged integer with object and returns true if their internal state is equal. /// /// First unsinged integer. /// Second big integer. /// True if equals. static public bool operator ==(uint int1, IntX int2) { return OpHelper.Cmp(int2, int1) == 0; } #endregion operator== #region operator!= /// /// Compares two objects and returns true if their internal state is not equal. /// /// First big integer. /// Second big integer. /// True if not equals. static public bool operator !=(IntX int1, IntX int2) { return OpHelper.Cmp(int1, int2, false) != 0; } /// /// Compares object with integer and returns true if their internal state is not equal. /// /// First big integer. /// Second integer. /// True if not equals. static public bool operator !=(IntX int1, int int2) { return OpHelper.Cmp(int1, int2) != 0; } /// /// Compares integer with object and returns true if their internal state is not equal. /// /// First integer. /// Second big integer. /// True if not equals. static public bool operator !=(int int1, IntX int2) { return OpHelper.Cmp(int2, int1) != 0; } /// /// Compares object with unsigned integer and returns true if their internal state is not equal. /// /// First big integer. /// Second unsigned integer. /// True if not equals. static public bool operator !=(IntX int1, uint int2) { return OpHelper.Cmp(int1, int2) != 0; } /// /// Compares unsigned integer with object and returns true if their internal state is not equal. /// /// First unsigned integer. /// Second big integer. /// True if not equals. static public bool operator !=(uint int1, IntX int2) { return OpHelper.Cmp(int2, int1) != 0; } #endregion operator!= #region operator> /// /// Compares two objects and returns true if first is greater. /// /// First big integer. /// Second big integer. /// True if first is greater. static public bool operator >(IntX int1, IntX int2) { return OpHelper.Cmp(int1, int2, true) > 0; } /// /// Compares object with integer and returns true if first is greater. /// /// First big integer. /// Second integer. /// True if first is greater. static public bool operator >(IntX int1, int int2) { return OpHelper.Cmp(int1, int2) > 0; } /// /// Compares integer with object and returns true if first is greater. /// /// First integer. /// Second big integer. /// True if first is greater. static public bool operator >(int int1, IntX int2) { return OpHelper.Cmp(int2, int1) < 0; } /// /// Compares object with unsigned integer and returns true if first is greater. /// /// First big integer. /// Second unsigned integer. /// True if first is greater. static public bool operator >(IntX int1, uint int2) { return OpHelper.Cmp(int1, int2) > 0; } /// /// Compares unsigned integer with object and returns true if first is greater. /// /// First unsigned integer. /// Second big integer. /// True if first is greater. static public bool operator >(uint int1, IntX int2) { return OpHelper.Cmp(int2, int1) < 0; } #endregion operator> #region operator>= /// /// Compares two objects and returns true if first is greater or equal. /// /// First big integer. /// Second big integer. /// True if first is greater or equal. static public bool operator >=(IntX int1, IntX int2) { return OpHelper.Cmp(int1, int2, true) >= 0; } /// /// Compares object with integer and returns true if first is greater or equal. /// /// First big integer. /// Second integer. /// True if first is greater or equal. static public bool operator >=(IntX int1, int int2) { return OpHelper.Cmp(int1, int2) >= 0; } /// /// Compares integer with object and returns true if first is greater or equal. /// /// First integer. /// Second big integer. /// True if first is greater or equal. static public bool operator >=(int int1, IntX int2) { return OpHelper.Cmp(int2, int1) <= 0; } /// /// Compares object with unsinged integer and returns true if first is greater or equal. /// /// First big integer. /// Second unsinged integer. /// True if first is greater or equal. static public bool operator >=(IntX int1, uint int2) { return OpHelper.Cmp(int1, int2) >= 0; } /// /// Compares unsinged integer with object and returns true if first is greater or equal. /// /// First unsinged integer. /// Second big integer. /// True if first is greater or equal. static public bool operator >=(uint int1, IntX int2) { return OpHelper.Cmp(int2, int1) <= 0; } #endregion operator>= #region operator< /// /// Compares two objects and returns true if first is lighter. /// /// First big integer. /// Second big integer. /// True if first is lighter. static public bool operator <(IntX int1, IntX int2) { return OpHelper.Cmp(int1, int2, true) < 0; } /// /// Compares object with integer and returns true if first is lighter. /// /// First big integer. /// Second integer. /// True if first is lighter. static public bool operator <(IntX int1, int int2) { return OpHelper.Cmp(int1, int2) < 0; } /// /// Compares integer with object and returns true if first is lighter. /// /// First integer. /// Second big integer. /// True if first is lighter. static public bool operator <(int int1, IntX int2) { return OpHelper.Cmp(int2, int1) > 0; } /// /// Compares object with unsinged integer and returns true if first is lighter. /// /// First big integer. /// Second unsinged integer. /// True if first is lighter. static public bool operator <(IntX int1, uint int2) { return OpHelper.Cmp(int1, int2) < 0; } /// /// Compares unsinged integer with object and returns true if first is lighter. /// /// First unsinged integer. /// Second big integer. /// True if first is lighter. static public bool operator <(uint int1, IntX int2) { return OpHelper.Cmp(int2, int1) > 0; } #endregion operator< #region operator<= /// /// Compares two objects and returns true if first is lighter or equal. /// /// First big integer. /// Second big integer. /// True if first is lighter or equal. static public bool operator <=(IntX int1, IntX int2) { return OpHelper.Cmp(int1, int2, true) <= 0; } /// /// Compares object with integer and returns true if first is lighter or equal. /// /// First big integer. /// Second integer. /// True if first is lighter or equal. static public bool operator <=(IntX int1, int int2) { return OpHelper.Cmp(int1, int2) <= 0; } /// /// Compares integer with object and returns true if first is lighter or equal. /// /// First integer. /// Second big integer. /// True if first is lighter or equal. static public bool operator <=(int int1, IntX int2) { return OpHelper.Cmp(int2, int1) >= 0; } /// /// Compares object with unsinged integer and returns true if first is lighter or equal. /// /// First big integer. /// Second unsinged integer. /// True if first is lighter or equal. static public bool operator <=(IntX int1, uint int2) { return OpHelper.Cmp(int1, int2) <= 0; } /// /// Compares unsinged integer with object and returns true if first is lighter or equal. /// /// First unsinged integer. /// Second big integer. /// True if first is lighter or equal. static public bool operator <=(uint int1, IntX int2) { return OpHelper.Cmp(int2, int1) >= 0; } #endregion operator<= #region operator+ and operator- /// /// Adds one object to another. /// /// First big integer. /// Second big integer. /// Addition result. static public IntX operator +(IntX int1, IntX int2) { return OpHelper.AddSub(int1, int2, false); } /// /// Subtracts one object from another. /// /// First big integer. /// Second big integer. /// Subtraction result. static public IntX operator -(IntX int1, IntX int2) { return OpHelper.AddSub(int1, int2, true); } #endregion operator+ and operator- #region operator* /// /// Multiplies one object on another. /// /// First big integer. /// Second big integer. /// Multiply result. static public IntX operator *(IntX int1, IntX int2) { return IntXMultiplier.Multiply(int1, int2); } #endregion operator* #region operator/ and operator% /// /// Divides one object by another. /// /// First big integer. /// Second big integer. /// Division result. static public IntX operator /(IntX int1, IntX int2) { IntX modRes; return IntXDivider.DivMod(int1, int2, out modRes, DivModResultFlags.Div); } /// /// Divides one object by another and returns division modulo. /// /// First big integer. /// Second big integer. /// Modulo result. static public IntX operator %(IntX int1, IntX int2) { IntX modRes; IntXDivider.DivMod(int1, int2, out modRes, DivModResultFlags.Mod); return modRes; } #endregion operator/ and operator% #region operator<< and operator>> /// /// Shifts object on selected bits count to the left. /// /// Big integer. /// Bits count. /// Shifting result. static public IntX operator <<(IntX intX, int shift) { return OpHelper.Sh(intX, shift, true); } /// /// Shifts object on selected bits count to the right. /// /// Big integer. /// Bits count. /// Shifting result. static public IntX operator >>(IntX intX, int shift) { return OpHelper.Sh(intX, shift, false); } #endregion operator<< and operator>> #region +, -, ++, -- unary operators /// /// Returns the same value. /// /// Initial value. /// The same value, but new object. /// is a null reference. static public IntX operator +(IntX value) { // Exception if (ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return new IntX(value); } /// /// Returns the same value, but with other sign. /// /// Initial value. /// The same value, but with other sign. /// is a null reference. static public IntX operator -(IntX value) { // Exception if (ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } IntX newValue = new IntX(value); if (newValue._length != 0) { newValue._negative = !newValue._negative; } return newValue; } /// /// Returns increased value. /// /// Initial value. /// Increased value. /// is a null reference. static public IntX operator ++(IntX value) { // Exception if (ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return value + 1U; } /// /// Returns decreased value. /// /// Initial value. /// Decreased value. /// is a null reference. static public IntX operator --(IntX value) { // Exception if (ReferenceEquals(value, null)) { throw new ArgumentNullException("value"); } return value - 1U; } #endregion +, -, ++, -- unary operators #region Conversion operators #region To IntX (Implicit) /// /// Implicitly converts to . /// /// Value to convert. /// Conversion result. static public implicit operator IntX(int value) { return new IntX(value); } /// /// Implicitly converts to . /// /// Value to convert. /// Conversion result. static public implicit operator IntX(uint value) { return new IntX(value); } /// /// Implicitly converts to . /// /// Value to convert. /// Conversion result. static public implicit operator IntX(ushort value) { return new IntX(value); } /// /// Implicitly converts to . /// /// Value to convert. /// Conversion result. static public implicit operator IntX(long value) { return new IntX(value); } /// /// Implicitly converts to . /// /// Value to convert. /// Conversion result. static public implicit operator IntX(ulong value) { return new IntX(value); } #endregion To IntX (Implicit) #region From IntX (Explicit) /// /// Explicitly converts to . /// /// Value to convert. /// Conversion result. static public explicit operator int(IntX value) { int res = (int)(uint)value; return value._negative ? -res : res; } /// /// Explicitly converts to . /// /// Value to convert. /// Conversion result. static public explicit operator uint(IntX value) { if (value == null) { throw new ArgumentNullException("value"); } if (value._length == 0) return 0; return value._digits[0]; } /// /// Explicitly converts to . /// /// Value to convert. /// Conversion result. static public explicit operator long(IntX value) { long res = (long)(ulong)value; return value._negative ? -res : res; } /// /// Explicitly converts to . /// /// Value to convert. /// Conversion result. static public explicit operator ulong(IntX value) { ulong res = (uint)value; if (value._length > 1) { res |= (ulong)value._digits[1] << Constants.DigitBitCount; } return res; } /// /// Explicitly converts to . /// /// Value to convert. /// Conversion result. static public explicit operator ushort(IntX value) { return (ushort)(uint)value; } #endregion From IntX (Explicit) #endregion Conversion operators #endregion Operators #region Math static methods #region Multiply /// /// Multiplies one object on another. /// /// First big integer. /// Second big integer. /// Multiply result. static public IntX Multiply(IntX int1, IntX int2) { return IntXMultiplier.Multiply(int1, int2); } #endregion Multiply #region Divide/modulo /// /// Divides one object by another. /// /// First big integer. /// Second big integer. /// Division result. static public IntX Divide(IntX int1, IntX int2) { IntX modRes; return IntXDivider.DivMod(int1, int2, out modRes, DivModResultFlags.Div); } /// /// Divides one object by another and returns division modulo. /// /// First big integer. /// Second big integer. /// Modulo result. static public IntX Modulo(IntX int1, IntX int2) { IntX modRes; IntXDivider.DivMod(int1, int2, out modRes, DivModResultFlags.Mod); return modRes; } /// /// Divides one object on another. /// Returns both divident and remainder /// /// First big integer. /// Second big integer. /// Remainder big integer. /// Division result. static public IntX DivideModulo(IntX int1, IntX int2, out IntX modRes) { return IntXDivider.DivMod(int1, int2, out modRes, DivModResultFlags.Div | DivModResultFlags.Mod); } #endregion Divide/modulo #region Pow /// /// Returns a specified big integer raised to the specified power. /// /// Number to raise. /// Power. /// Number in given power. static public IntX Pow(IntX value, uint power) { return OpHelper.Pow(value, power); } #endregion Pow #endregion Math static methods #region ToString override /// /// Returns decimal string representation of this object. /// /// Decimal number in string. override public string ToString() { return ToString(10U, true); } /// /// Returns string representation of this object in given base. /// /// Base of system in which to do output. /// Object string representation. public string ToString(uint numberBase) { return ToString(numberBase, true); } /// /// Returns string representation of this object in given base. /// /// Base of system in which to do output. /// Use uppercase for bases from 11 to 16 (which use letters A-F). /// Object string representation. public string ToString(uint numberBase, bool upperCase) { return IntXStringConverter.ToString(this, numberBase, upperCase ? Constants.BaseUpperChars : Constants.BaseLowerChars); } /// /// Returns string representation of this object in given base using custom alphabet. /// /// Base of system in which to do output. /// Alphabet which contains chars used to represent big integer, char position is coresponding digit value. /// Object string representation. public string ToString(uint numberBase, string alphabet) { StrRepHelper.AssertAlphabet(alphabet, numberBase); return IntXStringConverter.ToString(this, numberBase, alphabet.ToCharArray()); } #endregion ToString override #region Parsing methods /// /// Parses provided string representation of object in decimal base. /// If number starts from "0" then it's treated as octal; if number starts fropm "0x" /// then it's treated as hexadecimal. /// /// Number as string. /// Parsed object. static public IntX Parse(string value) { return IntXParser.Parse(value, 10U, Constants.BaseCharToDigits, true); } /// /// Parses provided string representation of object. /// /// Number as string. /// Number base. /// Parsed object. static public IntX Parse(string value, uint numberBase) { return IntXParser.Parse(value, numberBase, Constants.BaseCharToDigits, false); } /// /// Parses provided string representation of object using custom alphabet. /// /// Number as string. /// Number base. /// Alphabet which contains chars used to represent big integer, char position is coresponding digit value. /// Parsed object. static public IntX Parse(string value, uint numberBase, string alphabet) { return IntXParser.Parse(value, numberBase, StrRepHelper.CharDictionaryFromAlphabet(alphabet, numberBase), false); } #endregion Parsing methods #region IEquatable/Equals/GetHashCode implementation/overrides /// /// Returns equality of this with another big integer. /// /// Big integer to compare with. /// True if equals. public bool Equals(IntX n) { return base.Equals(n) || this == n; } /// /// Returns equality of this with another integer. /// /// Integer to compare with. /// True if equals. public bool Equals(int n) { return this == n; } /// /// Returns equality of this with another unsigned integer. /// /// Unsigned integer to compare with. /// True if equals. public bool Equals(uint n) { return this == n; } /// /// Returns equality of this with another long integer. /// /// Long integer to compare with. /// True if equals. public bool Equals(long n) { return this == n; } /// /// Returns equality of this with another unsigned long integer. /// /// Unsigned long integer to compare with. /// True if equals. public bool Equals(ulong n) { return this == n; } /// /// Returns equality of this with another object. /// /// Object to compare with. /// True if equals. override public bool Equals(object obj) { return obj is IntX && Equals((IntX)obj); } /// /// Returns hash code for this object. /// /// Object hash code. override public int GetHashCode() { switch (_length) { case 0: return 0; case 1: return (int)(_digits[0] ^ _length ^ (_negative ? 1 : 0)); default: return (int)(_digits[0] ^ _digits[_length - 1] ^ _length ^ (_negative ? 1 : 0)); } } #endregion Equals/GetHashCode implementation/overrides #region IComparable implementation /// /// Compares current object with another big integer. /// /// Big integer to compare with. /// 1 if object is bigger than , -1 if object is smaller than , 0 if they are equal. public int CompareTo(IntX n) { return OpHelper.Cmp(this, n, true); } /// /// Compares current object with another integer. /// /// Integer to compare with. /// 1 if object is bigger than , -1 if object is smaller than , 0 if they are equal. public int CompareTo(int n) { return OpHelper.Cmp(this, n); } /// /// Compares current object with another unsigned integer. /// /// Unsigned integer to compare with. /// 1 if object is bigger than , -1 if object is smaller than , 0 if they are equal. public int CompareTo(uint n) { return OpHelper.Cmp(this, n); } /// /// Compares current object with another long integer. /// /// Long integer to compare with. /// 1 if object is bigger than , -1 if object is smaller than , 0 if they are equal. public int CompareTo(long n) { return OpHelper.Cmp(this, n, true); } /// /// Compares current object with another unsigned long integer. /// /// Unsigned long integer to compare with. /// 1 if object is bigger than , -1 if object is smaller than , 0 if they are equal. public int CompareTo(ulong n) { return OpHelper.Cmp(this, n, true); } /// /// Compares current object with another object. /// /// Object to compare with. /// 1 if object is bigger than , -1 if object is smaller than , 0 if they are equal. public int CompareTo(object obj) { if (obj is IntX) { return CompareTo((IntX)obj); } else if (obj is int) { return CompareTo((int)obj); } else if (obj is uint) { return CompareTo((uint)obj); } else if (obj is long) { return CompareTo((long)obj); } else if (obj is ulong) { return CompareTo((ulong)obj); } throw new ArgumentException("Can't compare with provided argument.", "obj"); } #endregion IComparable implementation #region Other public methods /// /// Frees extra space not used by digits. /// public void Normalize() { if (_digits.LongLength > _length) { uint[] newDigits = new uint[_length]; Array.Copy(_digits, newDigits, _length); _digits = newDigits; } if (_length == 0) { _negative = false; } } /// /// Retrieves this internal state as digits array and sign. /// Can be used for serialization and other purposes. /// Note: please use constructor instead to clone object. /// /// Digits array. /// Is negative integer. public void GetInternalState(out uint[] digits, out bool negative) { digits = new uint[_length]; Array.Copy(_digits, digits, _length); negative = _negative; } #endregion Other public methods #region Init utilitary methods /// /// Initializes class instance from zero. /// For internal use. /// void InitFromZero() { _length = 0; _digits = new uint[0]; } /// /// Initializes class instance from value. /// Doesn't initialize sign. /// For internal use. /// /// Unsigned long value. void InitFromUlong(ulong value) { // Divide ulong into 2 uint values uint low = (uint)value; uint high = (uint)(value >> Constants.DigitBitCount); // Prepare internal fields if (high == 0) { _digits = new uint[] { low }; } else { _digits = new uint[] { low, high }; } _length = (uint)_digits.Length; } /// /// Initializes class instance from another value. /// For internal use. /// /// Big integer value. void InitFromIntX(IntX value) { _digits = value._digits; _length = value._length; _negative = value._negative; } /// /// Initializes class instance from digits array. /// For internal use. /// /// Big integer digits. /// Big integer sign. /// Big integer length. void InitFromDigits(uint[] digits, bool negative, uint length) { _digits = new uint[_length = length]; Array.Copy(digits, _digits, System.Math.Min((uint)digits.LongLength, length)); if (length != 0) { _negative = negative; } } #endregion Init utilitary methods #region Other utilitary methods /// /// Frees extra space not used by digits. /// internal void TryNormalize() { Normalize(); } #endregion Other utilitary methods #region IDisposable Implementation /// /// Disposes of the resources /// public void Dispose() { this._digits = new uint[0]; this._digits = null; System.GC.Collect(); } #endregion #region IClonable Implementation /// /// Creates a clone of the current instance. /// /// The cloned instance. object ICloneable.Clone() { return new IntX(this); } #endregion /// /// Converts this IntX to a byte array. /// /// public byte[] ToByteArray() { System.IO.MemoryStream m = new IO.MemoryStream(); System.IO.BinaryWriter b = new IO.BinaryWriter(m); for (int i = 0; i < _digits.Length; i++) { b.Write(_digits[i]); } b.Flush(); return m.GetBuffer(); } private static readonly IntX _zero = new IntX(0); /// /// Zero. /// public static IntX Zero { get { return _zero; } } } }