Exemplo n.º 1
0
 public static BigInt operator ^(BigInt a, uint b)
 {
     a.EnsureInitialized();
     return(BigInt.Power(a, b));
 }
Exemplo n.º 2
0
 public static void DivAndMod(BigInt a, BigInt b, out BigInt remainder, out BigInt result)
 {
     a.EnsureInitialized();
     b.EnsureInitialized();
     DivAndMod(DivModOperationType.DivAndMod, a, b, out remainder, out result);
 }
Exemplo n.º 3
0
        public static BigInt operator +(BigInt a, BigInt b)
        {
            a.EnsureInitialized();
            b.EnsureInitialized();

            bool   isSubtraction = false;
            BigInt result        = new BigInt(Math.Max(a.Capacity, b.Capacity), 0);

            if (a.Sign != b.Sign)
            {
                bool doSignSwapAddition = false;
                if (a.Sign == -1)
                {
                    doSignSwapAddition = (BigInt.CompareMagnitudes(a, b) == 1);
                }
                else //b.Sign == -1
                {
                    doSignSwapAddition = (BigInt.CompareMagnitudes(a, b) == -1);
                }
                if (doSignSwapAddition)
                {       // our standard addition algorithm doesn't work when a < 0 and b > 0 and |a| > |b| (or vice versa)
                    // becauase we don't look ahead to see if we can borrow, so instead calculate -((-a) + (-b))
                    a._sign = (short)-a.Sign;
                    b._sign = (short)-b.Sign;
                    result  = -(a + b);
                    a._sign = (short)-a.Sign;   // put signs back
                    b._sign = (short)-b.Sign;   // the way they were
                    return(result);
                }
                else
                {
                    isSubtraction = true;
                }
            }

            int currChunkSum = 0, carry = 0, currChunkToAppend;
            int numChunksToAdd = MaxNumChunks(a, b);

            for (int i = 0; i < numChunksToAdd; i++)
            {
                short borrow = 0, aChunk = a.GetChunk(i), bChunk = b.GetChunk(i);
                if (isSubtraction && ((a.Sign == 1 && (aChunk + carry < Math.Abs(bChunk))) ||
                                      (b.Sign == 1 && (bChunk + carry < Math.Abs(aChunk)))))
                {
                    //we're adding +ve and -ve, and the chunk in the positive number (combined with carry)
                    // is smaller than the chunk in the negative number, so have to borrow
                    borrow = 1000;
                }
                currChunkSum = aChunk + bChunk + borrow + carry;
                if (borrow > 0)
                {
                    carry             = -1;
                    currChunkToAppend = currChunkSum;
                }
                else if (currChunkSum >= 1000)
                {
                    carry             = 1;
                    currChunkToAppend = currChunkSum - 1000;
                }
                else if (currChunkSum <= -1000)
                {
                    carry             = -1;
                    currChunkToAppend = (-currChunkSum) - 1000;
                }
                else
                {
                    carry             = 0;
                    currChunkToAppend = Math.Abs(currChunkSum);
                }
                result.AppendChunk((ushort)currChunkToAppend);
            }
            if (carry != 0)
            {
                result.AppendChunk((ushort)Math.Abs(carry));
            }

            if (a._sign == b._sign)
            {
                result._sign = a._sign;
            }
            else
            {
                result._sign = (currChunkSum < 0) ? (short)-1 : (short)1;
            }

            result.AfterUpdate();
            return(result);
        }
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();
            }
        }