Exemple #1
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);
            }
        }
Exemple #2
0
        /// <summary>
        /// Compares two BigInts
        /// </summary>
        /// <returns>1 if a > b, 0 if a == b, -1 if a is less than b</returns>
        public static short Compare(BigInt a, BigInt b)
        {
            a.EnsureInitialized();
            b.EnsureInitialized();

            if (a.IsZero() && b.IsZero())
            {
                return(0);
            }

            if (a.Sign != b.Sign)
            {   //a and b have different signs
                if (a.Sign == 1)
                {
                    return(1);  //every positive number is bigger than every negative number
                }
                else
                {
                    return(-1); //and vice versa
                }
            }
            else if (a.NumDigits > b.NumDigits)
            {                   //a has more digits than b (a and b have same sign)
                return(a.Sign); // If both are positive then a > b.  If both are negative then a < b
            }
            else if (a.NumDigits < b.NumDigits)
            {                           //a has fewer digits than b (a and b have same sign)
                return((short)-a.Sign); // If both are positive then a < b.  If both are negative then a > b
            }
            else
            {
                short aChunk, bChunk;
                for (int i = a.NumChunks - 1; i >= 0; i--)
                {
                    aChunk = a.GetChunk(i);
                    bChunk = b.GetChunk(i);
                    if (aChunk < bChunk)
                    {
                        return(-1);
                    }
                    else if (aChunk > bChunk)
                    {
                        return(1);
                    }
                }
                //they must be equal
                return(0);
            }
        }
Exemple #3
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();
            }
        }