public static BigDecimal Add(BigDecimal value, BigDecimal augend, MathContext mc)
        {
            BigDecimal larger;             // operand with the largest unscaled value
            BigDecimal smaller;            // operand with the smallest unscaled value
            BigInteger tempBi;
            long       diffScale = (long)value.Scale - augend.Scale;

            // Some operand is zero or the precision is infinity
            if ((augend.IsZero) || (value.IsZero) || (mc.Precision == 0))
            {
                return(BigMath.Round(Add(value, augend), mc));
            }
            // Cases where there is room for optimizations
            if (value.AproxPrecision() < diffScale - 1)
            {
                larger  = augend;
                smaller = value;
            }
            else if (augend.AproxPrecision() < -diffScale - 1)
            {
                larger  = value;
                smaller = augend;
            }
            else
            {
                // No optimization is done
                return(BigMath.Round(Add(value, augend), mc));
            }
            if (mc.Precision >= larger.AproxPrecision())
            {
                // No optimization is done
                return(BigMath.Round(Add(value, augend), mc));
            }

            // Cases where it's unnecessary to add two numbers with very different scales
            var largerSignum = larger.Sign;

            if (largerSignum == smaller.Sign)
            {
                tempBi = Multiplication.MultiplyByPositiveInt(larger.UnscaledValue, 10) +
                         BigInteger.FromInt64(largerSignum);
            }
            else
            {
                tempBi = larger.UnscaledValue - BigInteger.FromInt64(largerSignum);
                tempBi = Multiplication.MultiplyByPositiveInt(tempBi, 10) +
                         BigInteger.FromInt64(largerSignum * 9);
            }
            // Rounding the improved adding
            larger = new BigDecimal(tempBi, larger.Scale + 1);
            return(BigMath.Round(larger, mc));
        }
        public static BigDecimal Subtract(BigDecimal value, BigDecimal subtrahend, MathContext mc)
        {
            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }
            if (mc == null)
            {
                throw new ArgumentNullException("mc");
            }

            long diffScale = subtrahend.Scale - (long)value.Scale;

            // Some operand is zero or the precision is infinity
            if ((subtrahend.IsZero) || (value.IsZero) || (mc.Precision == 0))
            {
                return(BigMath.Round(Subtract(value, subtrahend), mc));
            }

            // Now:   this != 0   and   subtrahend != 0
            if (subtrahend.AproxPrecision() < diffScale - 1)
            {
                // Cases where it is unnecessary to subtract two numbers with very different scales
                if (mc.Precision < value.AproxPrecision())
                {
                    var        thisSignum = value.Sign;
                    BigInteger tempBI;
                    if (thisSignum != subtrahend.Sign)
                    {
                        tempBI = Multiplication.MultiplyByPositiveInt(value.UnscaledValue, 10) +
                                 BigInteger.FromInt64(thisSignum);
                    }
                    else
                    {
                        tempBI = value.UnscaledValue - BigInteger.FromInt64(thisSignum);
                        tempBI = Multiplication.MultiplyByPositiveInt(tempBI, 10) +
                                 BigInteger.FromInt64(thisSignum * 9);
                    }
                    // Rounding the improved subtracting
                    var leftOperand = new BigDecimal(tempBI, value.Scale + 1);                     // it will be only the left operand (this)
                    return(BigMath.Round(leftOperand, mc));
                }
            }

            // No optimization is done
            return(BigMath.Round(Subtract(value, subtrahend), mc));
        }
        public static BigDecimal DivideToIntegralValue(BigDecimal dividend, BigDecimal divisor)
        {
            BigInteger integralValue;          // the integer of result
            BigInteger powerOfTen;             // some power of ten
            BigInteger quotient;
            BigInteger remainder;
            long       newScale  = (long)dividend.Scale - divisor.Scale;
            long       tempScale = 0;
            int        i         = 1;
            int        lastPow   = BigDecimal.TenPow.Length - 1;

            if (divisor.IsZero)
            {
                // math.04=Division by zero
                throw new ArithmeticException(Messages.math04);                 //$NON-NLS-1$
            }
            if ((divisor.AproxPrecision() + newScale > dividend.AproxPrecision() + 1L) ||
                (dividend.IsZero))
            {
                /* If the divisor's integer part is greater than this's integer part,
                 * the result must be zero with the appropriate scale */
                integralValue = BigInteger.Zero;
            }
            else if (newScale == 0)
            {
                integralValue = dividend.UnscaledValue / divisor.UnscaledValue;
            }
            else if (newScale > 0)
            {
                powerOfTen    = Multiplication.PowerOf10(newScale);
                integralValue = dividend.UnscaledValue / (divisor.UnscaledValue * powerOfTen);
                integralValue = integralValue * powerOfTen;
            }
            else
            {
                // (newScale < 0)
                powerOfTen    = Multiplication.PowerOf10(-newScale);
                integralValue = (dividend.UnscaledValue * powerOfTen) / divisor.UnscaledValue;
                // To strip trailing zeros approximating to the preferred scale
                while (!BigInteger.TestBit(integralValue, 0))
                {
                    quotient = BigMath.DivideAndRemainder(integralValue, BigDecimal.TenPow[i], out remainder);
                    if ((remainder.Sign == 0) &&
                        (tempScale - i >= newScale))
                    {
                        tempScale -= i;
                        if (i < lastPow)
                        {
                            i++;
                        }
                        integralValue = quotient;
                    }
                    else
                    {
                        if (i == 1)
                        {
                            break;
                        }
                        i = 1;
                    }
                }
                newScale = tempScale;
            }
            return((integralValue.Sign == 0)
                                ? BigDecimal.GetZeroScaledBy(newScale)
                                : new BigDecimal(integralValue, BigDecimal.ToIntScale(newScale)));
        }
        public static BigDecimal Divide(BigDecimal dividend, BigDecimal divisor, MathContext mc)
        {
            /* Calculating how many zeros must be append to 'dividend'
             * to obtain a  quotient with at least 'mc.precision()' digits */
            long traillingZeros = mc.Precision + 2L
                                  + divisor.AproxPrecision() - dividend.AproxPrecision();
            long       diffScale = (long)dividend.Scale - divisor.Scale;
            long       newScale  = diffScale;                  // scale of the final quotient
            int        compRem;                                // to compare the remainder
            int        i       = 1;                            // index
            int        lastPow = BigDecimal.TenPow.Length - 1; // last power of ten
            BigInteger integerQuot;                            // for temporal results
            BigInteger quotient = dividend.UnscaledValue;
            BigInteger remainder;

            // In special cases it reduces the problem to call the dual method
            if ((mc.Precision == 0) || (dividend.IsZero) || (divisor.IsZero))
            {
                return(Divide(dividend, divisor));
            }

            if (traillingZeros > 0)
            {
                // To append trailing zeros at end of dividend
                quotient  = dividend.UnscaledValue * Multiplication.PowerOf10(traillingZeros);
                newScale += traillingZeros;
            }
            quotient    = BigMath.DivideAndRemainder(quotient, divisor.UnscaledValue, out remainder);
            integerQuot = quotient;
            // Calculating the exact quotient with at least 'mc.precision()' digits
            if (remainder.Sign != 0)
            {
                // Checking if:   2 * remainder >= divisor ?
                compRem = remainder.ShiftLeftOneBit().CompareTo(divisor.UnscaledValue);
                // quot := quot * 10 + r;     with 'r' in {-6,-5,-4, 0,+4,+5,+6}
                integerQuot = (integerQuot * BigInteger.Ten) +
                              BigInteger.FromInt64(quotient.Sign * (5 + compRem));
                newScale++;
            }
            else
            {
                // To strip trailing zeros until the preferred scale is reached
                while (!BigInteger.TestBit(integerQuot, 0))
                {
                    quotient = BigMath.DivideAndRemainder(integerQuot, BigDecimal.TenPow[i], out remainder);
                    if ((remainder.Sign == 0) &&
                        (newScale - i >= diffScale))
                    {
                        newScale -= i;
                        if (i < lastPow)
                        {
                            i++;
                        }
                        integerQuot = quotient;
                    }
                    else
                    {
                        if (i == 1)
                        {
                            break;
                        }
                        i = 1;
                    }
                }
            }
            // To perform rounding
            return(new BigDecimal(integerQuot, BigDecimal.ToIntScale(newScale), mc));
        }