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 Pow(BigDecimal number, int n, MathContext mc)
        {
            // The ANSI standard X3.274-1996 algorithm
            int         m           = System.Math.Abs(n);
            int         mcPrecision = mc.Precision;
            int         elength     = (int)System.Math.Log10(m) + 1; // decimal digits in 'n'
            int         oneBitMask;                                  // mask of bits
            BigDecimal  accum;                                       // the single accumulator
            MathContext newPrecision = mc;                           // MathContext by default

            // In particular cases, it reduces the problem to call the other 'pow()'
            if ((n == 0) || ((number.IsZero) && (n > 0)))
            {
                return(Pow(number, n));
            }
            if ((m > 999999999) || ((mcPrecision == 0) && (n < 0)) ||
                ((mcPrecision > 0) && (elength > mcPrecision)))
            {
                // math.07=Invalid Operation
                throw new ArithmeticException(Messages.math07);                 //$NON-NLS-1$
            }
            if (mcPrecision > 0)
            {
                newPrecision = new MathContext(mcPrecision + elength + 1,
                                               mc.RoundingMode);
            }
            // The result is calculated as if 'n' were positive
            accum      = BigMath.Round(number, newPrecision);
            oneBitMask = Utils.HighestOneBit(m) >> 1;

            while (oneBitMask > 0)
            {
                accum = BigMath.Multiply(accum, accum, newPrecision);
                if ((m & oneBitMask) == oneBitMask)
                {
                    accum = BigMath.Multiply(accum, number, newPrecision);
                }
                oneBitMask >>= 1;
            }
            // If 'n' is negative, the value is divided into 'ONE'
            if (n < 0)
            {
                accum = Divide(BigDecimal.One, accum, newPrecision);
            }
            // The final value is rounded to the destination precision
            accum.InplaceRound(mc);
            return(accum);
        }
        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));
        }