예제 #1
0
        /// <summary>
        /// Compares 2 <see cref="IntX" /> objects.
        /// Returns "-2" if any argument is null, "-1" if <paramref name="int1" /> &lt; <paramref name="int2" />,
        /// "0" if equal and "1" if &gt;.
        /// </summary>
        /// <param name="int1">First big integer.</param>
        /// <param name="int2">Second big integer.</param>
        /// <param name="throwNullException">Raises or not <see cref="NullReferenceException" />.</param>
        /// <returns>Comparsion result.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="int1" /> or <paramref name="int2" /> is a null reference and <paramref name="throwNullException" /> is set to true.</exception>
        static public int Cmp(IntX int1, IntX int2, bool throwNullException)
        {
            // If one of the operands is null, throw exception or return -2
            bool isNull1 = ReferenceEquals(int1, null);
            bool isNull2 = ReferenceEquals(int2, null);

            if (isNull1 || isNull2)
            {
                if (throwNullException)
                {
                    throw new ArgumentNullException(isNull1 ? "int1" : "int2", Strings.CantBeNullCmp);
                }
                else
                {
                    return(isNull1 && isNull2 ? 0 : -2);
                }
            }

            // Compare sign
            if (int1._negative && !int2._negative)
            {
                return(-1);
            }
            if (!int1._negative && int2._negative)
            {
                return(1);
            }

            // Compare presentation
            return(DigitOpHelper.Cmp(int1._digits, int1._length, int2._digits, int2._length) * (int1._negative ? -1 : 1));
        }
예제 #2
0
        /// <summary>
        /// Converts digits from internal representaion into given base.
        /// </summary>
        /// <param name="digits">Big integer digits.</param>
        /// <param name="length">Big integer length.</param>
        /// <param name="numberBase">Base to use for output.</param>
        /// <param name="outputLength">Calculated output length (will be corrected inside).</param>
        /// <returns>Conversion result (later will be transformed to string).</returns>
        override public uint[] ToString(uint[] digits, uint length, uint numberBase, ref uint outputLength)
        {
            uint[] outputArray = base.ToString(digits, length, numberBase, ref outputLength);

            // Maybe base method already converted this number
            if (outputArray != null)
            {
                return(outputArray);
            }

            // Create an output array for storing of number in other base
            outputArray = new uint[outputLength + 1];

            // Make a copy of initial data
            uint[] digitsCopy = new uint[length];
            Array.Copy(digits, digitsCopy, length);

            // Calculate output numbers by dividing
            uint outputIndex;

            for (outputIndex = 0; length > 0; ++outputIndex)
            {
                length = DigitOpHelper.DivMod(digitsCopy, length, numberBase, digitsCopy, out outputArray[outputIndex]);
            }

            outputLength = outputIndex;
            return(outputArray);
        }
예제 #3
0
        /// <summary>
        /// Subtracts two big integers.
        /// </summary>
        /// <param name="int1">First big integer.</param>
        /// <param name="int2">Second big integer.</param>
        /// <returns>Resulting big integer.</returns>
        static public IntX Sub(IntX int1, IntX int2)
        {
            // Process zero values in special way
            if (int1._length == 0)
            {
                return(new IntX(int2._digits, true));
            }
            if (int2._length == 0)
            {
                return(new IntX(int1));
            }

            // Determine lower big int (without sign)
            IntX smallerInt;
            IntX biggerInt;
            int  compareResult = DigitOpHelper.Cmp(int1._digits, int1._length, int2._digits, int2._length);

            if (compareResult == 0)
            {
                return(new IntX());                                // integers are equal
            }
            if (compareResult < 0)
            {
                smallerInt = int1;
                biggerInt  = int2;
            }
            else
            {
                smallerInt = int2;
                biggerInt  = int1;
            }

            // Create new big int object
            IntX newInt = new IntX(biggerInt._length, ReferenceEquals(int1, smallerInt) ^ int1._negative);

            // Do actual subtraction
            newInt._length = DigitOpHelper.Sub(
                biggerInt._digits,
                biggerInt._length,
                smallerInt._digits,
                smallerInt._length,
                newInt._digits);

            // Normalization may be needed
            newInt.TryNormalize();

            return(newInt);
        }
예제 #4
0
        /// <summary>
        /// Adds two big integers.
        /// </summary>
        /// <param name="int1">First big integer.</param>
        /// <param name="int2">Second big integer.</param>
        /// <returns>Resulting big integer.</returns>
        /// <exception cref="ArgumentException"><paramref name="int1" /> or <paramref name="int2" /> is too big for add operation.</exception>
        static public IntX Add(IntX int1, IntX int2)
        {
            // Process zero values in special way
            if (int2._length == 0)
            {
                return(new IntX(int1));
            }
            if (int1._length == 0)
            {
                IntX x = new IntX(int2);
                x._negative = int1._negative;                 // always get sign of the first big integer
                return(x);
            }

            // Determine big int with lower length
            IntX smallerInt;
            IntX biggerInt;

            DigitHelper.GetMinMaxLengthObjects(int1, int2, out smallerInt, out biggerInt);

            // Check for add operation possibility
            if (biggerInt._length == uint.MaxValue)
            {
                throw new ArgumentException(Strings.IntegerTooBig);
            }

            // Create new big int object of needed length
            IntX newInt = new IntX(biggerInt._length + 1, int1._negative);

            // Do actual addition
            newInt._length = DigitOpHelper.Add(
                biggerInt._digits,
                biggerInt._length,
                smallerInt._digits,
                smallerInt._length,
                newInt._digits);

            // Normalization may be needed
            newInt.TryNormalize();

            return(newInt);
        }
예제 #5
0
        /// <summary>
        /// Shifts <see cref="IntX" /> object.
        /// Determines which operation to use basing on shift sign.
        /// </summary>
        /// <param name="intX">Big integer.</param>
        /// <param name="shift">Bits count to shift.</param>
        /// <param name="toLeft">If true the shifting to the left.</param>
        /// <returns>Bitwise shift operation result.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="intX" /> is a null reference.</exception>
        static public IntX Sh(IntX intX, long shift, bool toLeft)
        {
            // Exceptions
            if (ReferenceEquals(intX, null))
            {
                throw new ArgumentNullException("intX", Strings.CantBeNullOne);
            }

            // Zero can't be shifted
            if (intX._length == 0)
            {
                return(new IntX());
            }

            // Can't shift on zero value
            if (shift == 0)
            {
                return(new IntX(intX));
            }

            // Determine real bits count and direction
            ulong bitCount;
            bool  negativeShift;

            DigitHelper.ToUInt64WithSign(shift, out bitCount, out negativeShift);
            toLeft ^= negativeShift;

            // Get position of the most significant bit in intX and amount of bits in intX
            int   msb          = Bits.Msb(intX._digits[intX._length - 1]);
            ulong intXBitCount = (ulong)(intX._length - 1) * Constants.DigitBitCount + (ulong)msb + 1UL;

            // If shifting to the right and shift is too big then return zero
            if (!toLeft && bitCount >= intXBitCount)
            {
                if (intX.Negative)
                {
                    return(-1);
                }
                return(new IntX());
            }

            // Calculate new bit count
            ulong newBitCount = toLeft ? intXBitCount + bitCount : intXBitCount - bitCount;

            // If shifting to the left and shift is too big to fit in big integer, throw an exception
            if (toLeft && newBitCount > Constants.MaxBitCount)
            {
                throw new ArgumentException(Strings.IntegerTooBig, "intX");
            }

            // Get exact length of new big integer (no normalize is ever needed here).
            // Create new big integer with given length
            uint newLength = (uint)(newBitCount / Constants.DigitBitCount + (newBitCount % Constants.DigitBitCount == 0 ? 0UL : 1UL));
            IntX newInt    = new IntX(newLength, intX._negative);

            // Get full and small shift values
            uint fullDigits = (uint)(bitCount / Constants.DigitBitCount);
            int  smallShift = (int)(bitCount % Constants.DigitBitCount);

            // We can just copy (no shift) if small shift is zero
            if (smallShift == 0)
            {
                if (toLeft)
                {
                    Array.Copy(intX._digits, 0, newInt._digits, fullDigits, intX._length);
                }
                else
                {
                    Array.Copy(intX._digits, fullDigits, newInt._digits, 0, newLength);
                }
            }
            else
            {
                // Do copy with real shift in the needed direction
                if (toLeft)
                {
                    DigitOpHelper.Shr(intX._digits, 0, intX._length, newInt._digits, fullDigits + 1, Constants.DigitBitCount - smallShift, false);
                }
                else
                {
                    // If new result length is smaller then original length we shouldn't lose any digits
                    if (newLength < intX._length)
                    {
                        newLength++;
                    }

                    DigitOpHelper.Shr(intX._digits, fullDigits, newLength, newInt._digits, 0, smallShift, fullDigits + newLength > intX._length);
                }
            }

            if (!toLeft && intX.Negative)
            {
                return(~-newInt);
            }


            return(newInt);
        }
예제 #6
0
        /// <summary>
        /// Divides one <see cref="IntX" /> by another.
        /// </summary>
        /// <param name="int1">First big integer.</param>
        /// <param name="int2">Second big integer.</param>
        /// <param name="modRes">Remainder big integer.</param>
        /// <param name="resultFlags">Which operation results to return.</param>
        /// <returns>Divident big integer.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="int1" /> or <paramref name="int2" /> is a null reference.</exception>
        /// <exception cref="DivideByZeroException"><paramref name="int2" /> equals zero.</exception>
        virtual public IntX DivMod(IntX int1, IntX int2, out IntX modRes, DivModResultFlags resultFlags)
        {
            // Null reference exceptions
            if (ReferenceEquals(int1, null))
            {
                throw new ArgumentNullException("int1", Strings.CantBeNull);
            }
            else if (ReferenceEquals(int2, null))
            {
                throw new ArgumentNullException("int2", Strings.CantBeNull);
            }

            // Check if int2 equals zero
            if (int2._length == 0)
            {
                throw new DivideByZeroException();
            }

            // Get flags
            bool divNeeded = (resultFlags & DivModResultFlags.Div) != 0;
            bool modNeeded = (resultFlags & DivModResultFlags.Mod) != 0;

            // Special situation: check if int1 equals zero; in this case zero is always returned
            if (int1._length == 0)
            {
                modRes = modNeeded ? new IntX() : null;
                return(divNeeded ? new IntX() : null);
            }

            // Special situation: check if int2 equals one - nothing to divide in this case
            if (int2._length == 1 && int2._digits[0] == 1)
            {
                modRes = modNeeded ? new IntX() : null;
                return(divNeeded ? int2._negative ? -int1 : +int1 : null);
            }

            // Get resulting sign
            bool resultNegative = int1._negative ^ int2._negative;

            // Check if int1 > int2
            int compareResult = DigitOpHelper.Cmp(int1._digits, int1._length, int2._digits, int2._length);

            if (compareResult < 0)
            {
                modRes = modNeeded ? new IntX(int1) : null;
                return(divNeeded ? new IntX() : null);
            }
            else if (compareResult == 0)
            {
                modRes = modNeeded ? new IntX() : null;
                return(divNeeded ? new IntX(resultNegative ? -1 : 1) : null);
            }

            //
            // Actually divide here (by Knuth algorithm)
            //

            // Prepare divident (if needed)
            IntX divRes = null;

            if (divNeeded)
            {
                divRes = new IntX(int1._length - int2._length + 1U, resultNegative);
            }

            // Prepare mod (if needed)
            if (modNeeded)
            {
                modRes = new IntX(int1._length + 1U, int1._negative);
            }
            else
            {
                modRes = null;
            }

            // Call procedure itself
            uint modLength = int1._length;
            uint divLength = DivMod(
                int1._digits,
                modNeeded ? modRes._digits : null,
                ref modLength,
                int2._digits,
                null,
                int2._length,
                divNeeded ? divRes._digits : null,
                resultFlags,
                compareResult);

            // Maybe set new lengths and perform normalization
            if (divNeeded)
            {
                divRes._length = divLength;
                divRes.TryNormalize();
            }
            if (modNeeded)
            {
                modRes._length = modLength;
                modRes.TryNormalize();
            }

            // Return div
            return(divRes);
        }
예제 #7
0
        /// <summary>
        /// Divides two big integers.
        /// Also modifies <paramref name="digitsPtr1" /> and <paramref name="length1"/> (it will contain remainder).
        /// </summary>
        /// <param name="digitsPtr1">First big integer digits.</param>
        /// <param name="digitsBufferPtr1">Buffer for first big integer digits. May also contain remainder.</param>
        /// <param name="length1">First big integer length.</param>
        /// <param name="digitsPtr2">Second big integer digits.</param>
        /// <param name="digitsBufferPtr2">Buffer for second big integer digits. Only temporarily used.</param>
        /// <param name="length2">Second big integer length.</param>
        /// <param name="digitsResPtr">Resulting big integer digits.</param>
        /// <param name="resultFlags">Which operation results to return.</param>
        /// <param name="cmpResult">Big integers comparsion result (pass -2 if omitted).</param>
        /// <returns>Resulting big integer length.</returns>
        virtual unsafe public uint DivMod(
            uint *digitsPtr1,
            uint *digitsBufferPtr1,
            ref uint length1,
            uint *digitsPtr2,
            uint *digitsBufferPtr2,
            uint length2,
            uint *digitsResPtr,
            DivModResultFlags resultFlags,
            int cmpResult)
        {
            // Base implementation covers some special cases

            bool divNeeded = (resultFlags & DivModResultFlags.Div) != 0;
            bool modNeeded = (resultFlags & DivModResultFlags.Mod) != 0;

            //
            // Special cases
            //

            // Case when length1 == 0
            if (length1 == 0)
            {
                return(0);
            }

            // Case when both lengths are 1
            if (length1 == 1 && length2 == 1)
            {
                if (divNeeded)
                {
                    *digitsResPtr = *digitsPtr1 / *digitsPtr2;
                    if (*digitsResPtr == 0)
                    {
                        length2 = 0;
                    }
                }
                if (modNeeded)
                {
                    *digitsBufferPtr1 = *digitsPtr1 % *digitsPtr2;
                    if (*digitsBufferPtr1 == 0)
                    {
                        length1 = 0;
                    }
                }

                return(length2);
            }

            // Compare digits first (if was not previously compared)
            if (cmpResult == -2)
            {
                cmpResult = DigitOpHelper.Cmp(digitsPtr1, length1, digitsPtr2, length2);
            }

            // Case when first value is smaller then the second one - we will have remainder only
            if (cmpResult < 0)
            {
                // Maybe we should copy first digits into remainder (if remainder is needed at all)
                if (modNeeded)
                {
                    DigitHelper.DigitsBlockCopy(digitsPtr1, digitsBufferPtr1, length1);
                }

                // Zero as division result
                return(0);
            }

            // Case when values are equal
            if (cmpResult == 0)
            {
                // Maybe remainder must be marked as empty
                if (modNeeded)
                {
                    length1 = 0;
                }

                // One as division result
                if (divNeeded)
                {
                    *digitsResPtr = 1;
                }

                return(1);
            }

            // Case when second length equals to 1
            if (length2 == 1)
            {
                // Call method basing on fact if div is needed
                uint modRes;
                if (divNeeded)
                {
                    length2 = DigitOpHelper.DivMod(digitsPtr1, length1, *digitsPtr2, digitsResPtr, out modRes);
                }
                else
                {
                    modRes = DigitOpHelper.Mod(digitsPtr1, length1, *digitsPtr2);
                }

                // Maybe save mod result
                if (modNeeded)
                {
                    if (modRes != 0)
                    {
                        length1 = 1;
                        *digitsBufferPtr1 = modRes;
                    }
                    else
                    {
                        length1 = 0;
                    }
                }

                return(length2);
            }


            // This is regular case, not special
            return(uint.MaxValue);
        }
예제 #8
0
        /// <summary>
        /// Parses provided string representation of <see cref="IntX" /> object.
        /// </summary>
        /// <param name="value">Number as string.</param>
        /// <param name="startIndex">Index inside string from which to start.</param>
        /// <param name="endIndex">Index inside string on which to end.</param>
        /// <param name="numberBase">Number base.</param>
        /// <param name="charToDigits">Char->digit dictionary.</param>
        /// <param name="digitsRes">Resulting digits.</param>
        /// <returns>Parsed integer length.</returns>
        override unsafe public uint Parse(string value, int startIndex, int endIndex, uint numberBase, IDictionary <char, uint> charToDigits, uint[] digitsRes)
        {
            uint newLength = base.Parse(value, startIndex, endIndex, numberBase, charToDigits, digitsRes);

            // Maybe base method already parsed this number
            if (newLength != 0)
            {
                return(newLength);
            }

            // Check length - maybe use classic parser instead
            uint initialLength = (uint)digitsRes.LongLength;

            if (initialLength < Constants.FastParseLengthLowerBound || initialLength > Constants.FastParseLengthUpperBound)
            {
                return(_classicParser.Parse(value, startIndex, endIndex, numberBase, charToDigits, digitsRes));
            }

            uint valueLength  = (uint)(endIndex - startIndex + 1);
            uint digitsLength = 1U << Bits.CeilLog2(valueLength);

            // Prepare array for digits in other base
            uint[] valueDigits = ArrayPool <uint> .Instance.GetArray(digitsLength);

            // This second array will store integer lengths initially
            uint[] valueDigits2 = ArrayPool <uint> .Instance.GetArray(digitsLength);

            fixed(uint *valueDigitsStartPtr = valueDigits, valueDigitsStartPtr2 = valueDigits2)
            {
                // In the string first digit means last in digits array
                uint *valueDigitsPtr  = valueDigitsStartPtr + valueLength - 1;
                uint *valueDigitsPtr2 = valueDigitsStartPtr2 + valueLength - 1;

                // Reverse copy characters into digits
                fixed(char *valueStartPtr = value)
                {
                    char *valuePtr    = valueStartPtr + startIndex;
                    char *valueEndPtr = valuePtr + valueLength;

                    for (; valuePtr < valueEndPtr; ++valuePtr, --valueDigitsPtr, --valueDigitsPtr2)
                    {
                        // Get digit itself - this call will throw an exception if char is invalid
                        *valueDigitsPtr = StrRepHelper.GetDigit(charToDigits, *valuePtr, numberBase);

                        // Set length of this digit (zero for zero)
                        *valueDigitsPtr2 = *valueDigitsPtr == 0U ? 0U : 1U;
                    }
                }

                // We have retrieved lengths array from pool - it needs to be cleared before using
                DigitHelper.SetBlockDigits(valueDigitsStartPtr2 + valueLength, digitsLength - valueLength, 0);

                // Now start from the digit arrays beginning
                valueDigitsPtr  = valueDigitsStartPtr;
                valueDigitsPtr2 = valueDigitsStartPtr2;

                // Current multiplier (classic or fast) will be used
                IMultiplier multiplier = MultiplyManager.GetCurrentMultiplier();

                // Here base in needed power will be stored
                IntX baseInt = null;

                // Temporary variables used on swapping
                uint[] tempDigits;
                uint * tempPtr;

                // Variables used in cycle
                uint *ptr1, ptr2, valueDigitsPtrEnd;
                uint  loLength, hiLength;

                // Outer cycle instead of recursion
                for (uint innerStep = 1, outerStep = 2; innerStep < digitsLength; innerStep <<= 1, outerStep <<= 1)
                {
                    // Maybe baseInt must be multiplied by itself
                    baseInt = baseInt == null ? numberBase : baseInt * baseInt;

                    // Using unsafe here
                    fixed(uint *baseDigitsPtr = baseInt._digits)
                    {
                        // Start from arrays beginning
                        ptr1 = valueDigitsPtr;
                        ptr2 = valueDigitsPtr2;

                        // vauleDigits array end
                        valueDigitsPtrEnd = valueDigitsPtr + digitsLength;

                        // Cycle thru all digits and their lengths
                        for (; ptr1 < valueDigitsPtrEnd; ptr1 += outerStep, ptr2 += outerStep)
                        {
                            // Get lengths of "lower" and "higher" value parts
                            loLength = *ptr2;
                            hiLength = *(ptr2 + innerStep);

                            if (hiLength != 0)
                            {
                                // We always must clear an array before multiply
                                DigitHelper.SetBlockDigits(ptr2, outerStep, 0U);

                                // Multiply per baseInt
                                hiLength = multiplier.Multiply(
                                    baseDigitsPtr,
                                    baseInt._length,
                                    ptr1 + innerStep,
                                    hiLength,
                                    ptr2);
                            }

                            // Sum results
                            if (hiLength != 0 || loLength != 0)
                            {
                                *ptr1 = DigitOpHelper.Add(
                                    ptr2,
                                    hiLength,
                                    ptr1,
                                    loLength,
                                    ptr2);
                            }
                            else
                            {
                                *ptr1 = 0U;
                            }
                        }
                    }

                    // After inner cycle valueDigits will contain lengths and valueDigits2 will contain actual values
                    // so we need to swap them here
                    tempDigits   = valueDigits;
                    valueDigits  = valueDigits2;
                    valueDigits2 = tempDigits;

                    tempPtr         = valueDigitsPtr;
                    valueDigitsPtr  = valueDigitsPtr2;
                    valueDigitsPtr2 = tempPtr;
                }
            }

            // Determine real length of converted number
            uint realLength = valueDigits2[0];

            // Copy to result
            Array.Copy(valueDigits, digitsRes, realLength);

            // Return arrays to pool
            ArrayPool <uint> .Instance.AddArray(valueDigits);

            ArrayPool <uint> .Instance.AddArray(valueDigits2);

            return(realLength);
        }