Exemplo n.º 1
0
        private static BigInt DivideAndConquerMultiply(BigInt a, BigInt b)
        {
            //faster divide-and-conquer multiplication, called by operator* when a and b are
            //large.  Do not call directly.

            int numChunks = MaxNumChunks(a, b);

            if (MinNumChunks(a, b) < D_AND_C_THRESHOLD)
            {
                return(a * b);
            }

            if ((numChunks & 1) == 1)  // if numChunks is odd, add 1
            {
                numChunks++;
            }

            int N       = numChunks / 2;
            int Ndigits = N + N + N; // N chunks = 3N digits
            //set a0,a1,b0,b1 such that a = (1000^N)*A1 + A0, b = (1000^N)*B1 + B0
            //then a*b = (1000^(2N) + 1000^N)A1B1 + 1000^N(A1-A0)(B0-B1) + (1000^N + 1)A0B0

            int    capacity = StorageSizeForDigits(a.NumDigits + b.NumDigits);
            BigInt a0       = new BigInt(capacity, 0);
            BigInt a1       = new BigInt(capacity, 0);
            BigInt b0       = new BigInt(capacity, 0);
            BigInt b1       = new BigInt(capacity, 0);

            for (int i = 0; i < N; i++)
            {
                a0.AppendChunk((ushort)a.GetChunkAbsolute(i));
                a1.AppendChunk((ushort)a.GetChunkAbsolute(i + N));
                b0.AppendChunk((ushort)b.GetChunkAbsolute(i));
                b1.AppendChunk((ushort)b.GetChunkAbsolute(i + N));
            }

            a0.AfterUpdate(); //TODO: get rid of these? and call result.AfterUpdate?
            a1.AfterUpdate();
            b0.AfterUpdate();
            b1.AfterUpdate();

            BigInt part1 = DivideAndConquerMultiply(a1, b1);
            BigInt part2 = DivideAndConquerMultiply(a1 - a0, b0 - b1);
            BigInt part3 = DivideAndConquerMultiply(a0, b0);

            BigInt result = (part1 << 2 * Ndigits) + (part1 << Ndigits) + (part2 << Ndigits) + (part3 << Ndigits) + part3;

            result._sign = (short)(a.Sign * b.Sign); //-ve * -ve == +ve, etc
            return(result);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Compares the absolute values of two BigInts
        /// </summary>
        /// <returns>1 if |a| > |b|, 0 if |a| == |b|, -1 if |a| is less than |b|</returns>
        private static short CompareMagnitudes(BigInt a, BigInt b)
        {
            if (a.IsZero() && b.IsZero())
            {
                return(0);
            }

            if (a.NumDigits > b.NumDigits)
            {   //a has more digits than b so it must be bigger
                return((short)1);
            }
            else if (a.NumDigits < b.NumDigits)
            {   //a has fewer digits than b so it must be smaller
                return((short)-1);
            }
            else
            {
                short aChunk, bChunk;
                for (int i = a.NumChunks - 1; i >= 0; i--)
                {
                    aChunk = a.GetChunkAbsolute(i);
                    bChunk = b.GetChunkAbsolute(i);
                    if (aChunk < bChunk)
                    {
                        return(-1);
                    }
                    else if (aChunk > bChunk)
                    {
                        return(1);
                    }
                }
                //they must be equal
                return(0);
            }
        }
Exemplo n.º 3
0
        public static BigInt ModPower(BigInt a, BigInt b, BigInt c)
        {       // returns (a ^ b) mod c
            // eg 28 ^ 23 mod 55 = 7

            // There's a trick to this.  a ^ b is usually way too big to handle, so we break it
            // into pieces, using the fact that xy mod c = ( x mod c )( y mod c ) mod c.
            // Please see www.carljohansen.co.uk/rsa for a full explanation.

            a.EnsureInitialized();
            b.EnsureInitialized();
            c.EnsureInitialized();

            int    numBdigits = b.NumDigits;
            BigInt prevResult, finalProduct = 1;
            int    i;

            BigInt[] arrResidues = new BigInt[numBdigits + 1];
            BigInt   accumulation2, accumulation4, accumulation8;

            arrResidues[0] = a;
            prevResult     = arrResidues[0];

            for (i = 1; i < numBdigits; i++)
            {
                //have to calculate (prevResult^10) mod c.  This is (prevResult^2^2^2) mod c * (prevResult^2) mod c
                accumulation2 = (prevResult * prevResult) % c;
                accumulation4 = (accumulation2 * accumulation2) % c;
                accumulation8 = (accumulation4 * accumulation4) % c;

                arrResidues[i] = (accumulation2 * accumulation8) % c;
                prevResult     = arrResidues[i];
            }

            for (i = numBdigits - 1; i >= 0; i--)
            {
                int currChunk = b.GetChunkAbsolute(i / 3);
                int currDigit;
                if (i % 3 == 0)
                {
                    currDigit = currChunk % 10;
                }
                else if (i % 3 == 1)
                {
                    currDigit = (currChunk % 100) / 10;
                }
                else
                {
                    currDigit = currChunk / 100;
                }
                for (int j = 0; j < currDigit; j++)
                {
                    finalProduct *= arrResidues[i];
                    finalProduct %= c;
                }
            }

            return(finalProduct);
        }
Exemplo n.º 4
0
        private static void DivAndMod(DivModOperationType operationType, BigInt a, BigInt b, out BigInt remainder, out BigInt result)
        // sets <result> to the result of dividing a by b, and sets <remainder> to the remainder when
        // a is divided by b
        {
            if (b.IsZero())
            {
                throw new DivideByZeroException();
            }

            if (CompareMagnitudes(a, b) < 0)  // if a is less than b then it's a no-brainer
            {
                if (operationType != DivModOperationType.DivideOnly)
                {
                    remainder = a;
                }
                else
                {
                    remainder = 0;
                }
                result = 0;
                return;
            }

            if (a.NumChunks <= 3)
            {   //a, b small - just use machine arithmetic
                remainder = 0;
                int intA = Convert.ToInt32(a.ToString());
                int intB = Convert.ToInt32(b.ToString());
                if (operationType != DivModOperationType.DivideOnly)
                {
                    remainder = intA % intB;
                }
                if (operationType != DivModOperationType.ModulusOnly)
                {
                    result = intA / intB;
                }
                else
                {
                    result = 0;
                }
                return;
            }

            result = new BigInt(a.Capacity, 0);

            BigInt removal, resultStore = new BigInt(a.Capacity, 0);

            short  resultSign = (short)(a.Sign * b.Sign);
            short  bSign      = b.Sign;
            ushort trialDiv;
            short  comparison;
            int    numBdigits = b.NumDigits;

            remainder       = a;
            remainder._sign = 1;
            b._sign         = 1;                                // work with positive numbers

            int bTrial = 0;

            if (b.NumChunks == 1)
            {
                bTrial = b.GetChunkAbsolute(b.NumChunks - 1);
            }
            else
            {
                bTrial = b.GetChunkAbsolute(b.NumChunks - 1) * 1000 + b.GetChunkAbsolute(b.NumChunks - 2);
            }

            int bTrialDigits = (int)Math.Floor(Math.Log10(bTrial)) + 1;
            int thisShift = 0, lastShift = -1;


            while (remainder >= b)
            {
                int   lastRemainderDigits = remainder.NumDigits;
                int   remTrial;
                short remMostSigChunk = remainder.GetChunkAbsolute(remainder._numChunks - 1);
                //remTrial is formed by taking the most significant 1, 2 or 3 chunks of remainder;
                // take the minimum number of chunks needed to make remTrial bigger than bTrial.
                if (remMostSigChunk >= bTrial)
                {
                    remTrial = remMostSigChunk;
                }
                else
                {
                    remTrial = remMostSigChunk * 1000 + remainder.GetChunkAbsolute(remainder.NumChunks - 2);
                    if (remTrial < bTrial)
                    {
                        remTrial = (remTrial * 1000) + remainder.GetChunkAbsolute(remainder.NumChunks - 3);
                    }
                    //now remTrial should be >= bTrial.  remTrial == bTrial is the nasty special case that produces trialDiv == 1.
                }

                trialDiv = (ushort)(remTrial / bTrial); // this is our guess of the next quotient chunk
                removal  = b.ShortMultiply(trialDiv);
                int remTrialDigits = (int)Math.Floor(Math.Log10(remTrial)) + 1;
                thisShift = remainder.NumDigits - (remTrialDigits - bTrialDigits) - numBdigits;
                removal   = removal << thisShift;

                // normally, the required shift drops by three (the number of digits in a chunk) with
                // each loop iteration.  But if the result contains a 000 chunk in the middle, we
                // will notice a drop in shift of more than 3.
                if (operationType != DivModOperationType.ModulusOnly)
                {
                    for (int i = 0; i < lastShift - thisShift - 3; i += 3)
                    {
                        resultStore.AppendChunk(0);
                    }
                }

                // our guess of the next quotient chunk might be too high (but not by more than two);
                // adjust it down if necessary
                BigInt bShifted    = 0;
                bool   bShiftedSet = false;
                do
                {
                    comparison = Compare(removal, remainder);
                    if (comparison > 0)
                    {
                        trialDiv--;
                        if (trialDiv == 0)
                        {
                            //This is the nasty special case.  I think that the first three cases can
                            // never happen.  Effectively what happens is that have guessed 1000 (through
                            // trialDiv = 1 and thisShift >= 3) and found that it is one high so we
                            // reduce it to 999 (through trialDiv = 999 and thisShift -= 3).
                            switch (thisShift)
                            {
                            case 0:     //This should never happen
                                break;

                            case 1:
                                trialDiv  = 9;
                                thisShift = 0;
                                break;

                            case 2:
                                trialDiv  = 99;
                                thisShift = 0;
                                break;

                            default:
                                trialDiv   = 999;
                                thisShift -= 3;
                                break;
                            }
                        }
                        if (!bShiftedSet)
                        {
                            bShiftedSet = true;
                            bShifted    = b << thisShift;
                        }
                        removal -= bShifted;
                    }
                } while (comparison > 0);

                lastShift = thisShift;

                remainder -= removal;
                if (operationType != DivModOperationType.ModulusOnly)
                {
                    resultStore.AppendChunk(trialDiv);
                }
            }

            b._sign = bSign; // put back b's sign

            if (operationType != DivModOperationType.DivideOnly)
            {
                remainder._sign = a.Sign; // sign of mod = sign of a
                remainder.AfterUpdate();
            }

            if (operationType != DivModOperationType.ModulusOnly)
            {
                for (int i = 3; i <= thisShift; i += 3)
                {
                    resultStore.AppendChunk(0);
                }
                //resultStore now holds the quotient chunks in reverse order.  Flip them and return.
                for (int i = resultStore.NumChunks - 1; i >= 0; i--)
                {
                    result.AppendChunk((ushort)resultStore.GetChunkAbsolute(i));
                }
                result._sign = resultSign;
                result.AfterUpdate();
            }
        }