public Fraction(Fraction/*!*/ copyFrom, int maxPrecision)
     : this(maxPrecision, maxPrecision)
 {
     // Copy the fraction
     int length = System.Math.Min(maxPrecision, copyFrom.MaxPrecision);
     Array.Copy(copyFrom._words, _words, length);
 }
        private static Fraction/*!*/ Normalize(Fraction/*!*/ f, out int digitOffset)
        {
            // Count leading zero words
            int leadingZeroWords = 0;
            while (leadingZeroWords < f.MaxPrecision && f._words[leadingZeroWords] == 0)
            {
                leadingZeroWords++;
            }

            if (leadingZeroWords == f.MaxPrecision)
            {
                // The fraction is just all zeros
                digitOffset = 0;
                return f;
            }

            // Count trailing zero words
            int trailingZeroWords = 0;
            while (f._words[f.Precision - 1 - trailingZeroWords] == 0)
            {
                trailingZeroWords++;
            }
            // Count leading zero digits (within the first non-zero word)
            // and also calculate up and down factors for masking
            int leadingZeroDigits = BASE_FIG;
            uint firstWord = f._words[leadingZeroWords];
            uint downFactor = 1;
            while (firstWord != 0)
            {
                firstWord /= 10;
                leadingZeroDigits--;
                downFactor *= 10;
            }
            uint upFactor = BASE / downFactor;

            int newPrecision = f.Precision - leadingZeroWords - trailingZeroWords;
            Fraction n = new Fraction(newPrecision);
            // Copy the non-zero words across
            Array.Copy(f._words, leadingZeroWords, n._words, 0, n.MaxPrecision);

            if (leadingZeroDigits > 0)
            {
                // Scroll the digits within the non-zero words to trim off the first zero digits
                uint bottomDigits = n._words[0] * upFactor;
                uint topDigits = 0;
                int wordIndex = 1;
                while (wordIndex < n.Precision)
                {
                    topDigits = n._words[wordIndex] / downFactor;
                    n._words[wordIndex - 1] = bottomDigits + topDigits;
                    bottomDigits = (n._words[wordIndex] % downFactor) * upFactor;
                    wordIndex++;
                }

                // Fix up the last word
                n._words[wordIndex - 1] = bottomDigits;
                // and the Precision
                n.Precision = wordIndex;
            }


            // Return the offset in digits caused by the normalization.
            digitOffset = -(leadingZeroWords * BASE_FIG + leadingZeroDigits);
            return n;
        }
        private static Fraction/*!*/ Parse(string/*!*/ digits, out int digitOffset)
        {
            // Trim off any trailing zeros
            digits = digits.TrimEnd('0');

            if (digits == "")
            {
                digitOffset = 0;
                return Zero;
            }

            // Create the new fraction
            int precision = digits.Length / BASE_FIG;
            int finalDigits = digits.Length % BASE_FIG;
            if (finalDigits > 0) { ++precision; }
            Fraction/*!*/ fraction = new Fraction(precision);

            // Iterate through groups of BASE_FIG digits
            int digitIndex;
            int wordIndex = 0;
            for (digitIndex = 0; digitIndex + BASE_FIG <= digits.Length; digitIndex += BASE_FIG)
            {
                fraction._words[wordIndex] = uint.Parse(digits.Substring(digitIndex, BASE_FIG), CultureInfo.InvariantCulture);
                wordIndex++;
            }

            // Add on the last few digits, adding extra zeros as necessary
            if (finalDigits > 0)
            {
                uint lastWord = uint.Parse(digits.Substring(digitIndex), CultureInfo.InvariantCulture);
                while (finalDigits < BASE_FIG)
                {
                    lastWord *= 10;
                    ++finalDigits;
                }
                fraction._words[wordIndex] = lastWord;
            }

            fraction = Fraction.Normalize(fraction, out digitOffset);
            return fraction;
        }
        /// <summary>
        /// Limits the precision of the given Fraction.
        /// </summary>
        /// <param name="sign">The sign of the BigDecimal</param>
        /// <param name="fraction">The fraction to limit</param>
        /// <param name="digits">The number of digits to which we are limiting</param>
        /// <param name="mode">The rounding mode to use when limiting</param>
        /// <returns>A new fraction that has no more than <paramref name="digits"/> digits.</returns>
        /// <example>
        /// Consider a fraction of 123456789 using default HalfUp rounding.
        /// Limit : Result
        /// 1       1
        /// 2       12
        /// 3       123
        /// 4       1234
        /// 5       12346
        /// 6       123457
        /// 7       1234568
        /// 8       12345679
        /// 9       123456789
        /// 10      123456789
        /// </example>
        public static Fraction/*!*/ LimitPrecision(int sign, Fraction/*!*/ fraction, int digits, BigDecimal.RoundingModes mode, out int offset)
        {
            Fraction result;

            if (digits <= 0)
            {
                uint digit = (uint)(fraction.IsZero ? 0 : 1);
                if (RoundDigit(sign, digit, 0, mode) > 0)
                {
                    offset = 1 - digits;
                    return One;
                }
                else
                {
                    offset = 0;
                    return Zero;
                }
            }

            if (digits >= fraction.DigitCount)
            {
                offset = 0;
                return fraction;
            }

            // Calculate offsets of relevant digits
            int secondLastDigitIndex; // i.e. fraction[digits-1]
            int secondLastWordIndex;
            uint secondLastDigit;
            int lastDigitIndex; // i.e. fraction[digits]
            int lastWordIndex;
            uint lastDigit;

#if SILVERLIGHT
            secondLastWordIndex = DivRem(digits - 1, BASE_FIG, out secondLastDigitIndex);
#else
            secondLastWordIndex = System.Math.DivRem(digits - 1, BASE_FIG, out secondLastDigitIndex);
#endif
            if (secondLastDigitIndex == BASE_FIG - 1)
            {
                lastWordIndex = secondLastWordIndex + 1;
                lastDigitIndex = 0;
            }
            else
            {
                lastWordIndex = secondLastWordIndex;
                lastDigitIndex = secondLastDigitIndex + 1;
            }

            // TODO: Extract these calculations out into static readonly arrays
            // Mask for last digit.  E.g. lastDigitIndex = 3, BASE_FIG = 9 => lastFactor = 1000000
            uint lastFactor = _powers[lastDigitIndex];
            // Mask for second last digit
            uint secondLastFactor = _powers[secondLastDigitIndex];

            // Calculate the current digits and rounding direction
            secondLastDigit = (fraction._words[secondLastWordIndex] / secondLastFactor) % 10;
            if (lastWordIndex < fraction.MaxPrecision)
            {
                lastDigit = (fraction._words[lastWordIndex] / lastFactor) % 10;
            }
            else
            {
                lastDigit = 0;
            }
            int round = RoundDigit(sign, lastDigit, secondLastDigit, mode);

            // Create a temporary fraction used to cause the rounding in the original
            result = new Fraction(lastWordIndex + 1);
            Array.Copy(fraction._words, 0, result._words, 0, System.Math.Min(lastWordIndex + 1, fraction.Precision));
            // Clear the digits beyond the second last digit
            result._words[secondLastWordIndex] = result._words[secondLastWordIndex] - (fraction._words[secondLastWordIndex] % secondLastFactor);
            if (round > 0)
            {
                // Increment the last digit of result by 1
                Fraction temp = new Fraction(secondLastWordIndex + 1);
                temp._words[secondLastWordIndex] = secondLastFactor;
                result = Fraction.Add(result, temp, 0, out offset);
            }
            else
            {
                offset = 0;
            }

            result.Precision = System.Math.Min(secondLastWordIndex + 1, result.MaxPrecision);
            return result;
        }
        // Note: Scrolling left is just the same as scrolling right by (BASE_FIG-offset)
        private static Fraction/*!*/ ScrollRight(Fraction/*!*/ fraction, int offset)
        {
            Debug.Assert(offset <= BASE_FIG);
            Debug.Assert(offset >= 0);

            // Don't do anything if offset is not going to change the internal digit layout
            if (offset % BASE_FIG == 0)
            {
                return fraction;
            }

            int oldPrecision = fraction.Precision;
            int newPrecision = oldPrecision + 1;

            Fraction newFraction = new Fraction(newPrecision);

            // Calculate masking values
            // divisor is used to mask off and bring top digits down to bottom digits (TTTTTTBBBB / divisor == TTTTTT)
            // also divisor is used to mask off the bottom digits (TTTTTTBBBB % divisor == BBBB)
            // factor is then used to bring bottom digits up to top digits (BBBB * factor == BBBB000000)
            uint downFactor = 1;
            for (int i = 0; i < offset; ++i)
            {
                downFactor *= 10;
            }
            uint upFactor = BASE / downFactor;

            // Scroll the digits
            // We want to go from TTTTTTBBBB TTTTTTBBBB TTTTTTBBBB to 0000TTTTTT BBBBTTTTTT BBBBTTTTTT BBBB000000
            // I.E. We are pushing all the digits to the right by "offset" amount and padding with zeros
            uint topDigits = 0;
            uint bottomDigits = 0;
            int wordIndex = 0;
            while (wordIndex < oldPrecision)
            {
                topDigits = fraction._words[wordIndex] / downFactor;
                newFraction._words[wordIndex] = bottomDigits + topDigits;
                bottomDigits = (fraction._words[wordIndex] % downFactor) * upFactor;
                wordIndex++;
            }

            // Fix up the last word
            newFraction._words[wordIndex] = bottomDigits;

            return newFraction;
        }
 public static int Compare(Fraction/*!*/ x, Fraction/*!*/ y, int exponentDiff)
 {
     if (exponentDiff != 0)
     {
         return exponentDiff > 0 ? 1 : -1;
     }
     int wordIndex = 0;
     while (wordIndex < x.Precision && wordIndex < y.Precision)
     {
         if (x._words[wordIndex] != y._words[wordIndex])
         {
             return x._words[wordIndex] > y._words[wordIndex] ? 1 : -1;
         }
         wordIndex++;
     }
     if (wordIndex == x.Precision)
     {
         while (wordIndex < y.Precision)
         {
             if (y._words[wordIndex] != 0)
             {
                 return -1;
             }
             wordIndex++;
         }
     }
     else
     {
         while (wordIndex < x.Precision)
         {
             if (x._words[wordIndex] != 0)
             {
                 return 1;
             }
             wordIndex++;
         }
     }
     return 0;
 }
        public static Fraction/*!*/ Divide(Fraction/*!*/ a, Fraction/*!*/ b, int minPrecision, out Fraction/*!*/ r, out int cOffset, out int rOffset)
        {
            int precision = System.Math.Max(a.MaxPrecision + b.MaxPrecision + 1, minPrecision);
            Fraction c = new Fraction(precision);
            r = new Fraction(precision * 2);

            uint[] aWords = a._words;
            uint[] bWords = b._words;
            uint[] cWords = c._words;
            uint[] rWords = r._words;

            int aSize = a.Precision;
            int bSize = b.Precision;
            int cSize = c.MaxPrecision;
            int rSize = r.MaxPrecision;

            // Initialize remainder (we add an extra word at the front to catch the overflow
            Array.Copy(aWords, 0, rWords, 1, System.Math.Min(aSize, rSize - 1));

            // Setup basic values
            ulong b1 = bWords[0];
            ulong b1plus1 = bSize <= 1 ? b1 : b1 + 1;
            ulong b1b2 = bSize <= 1 ? (ulong)bWords[0] * BASE : GetDoubleWord(bWords, 0, b.Precision);
            ulong b1b2plus1 = bSize <= 2 ? b1b2 : b1b2 + 1;

            // Main loop
            int index = 1;
            int size = System.Math.Min(cSize, rSize);
            while (index < size)
            {
                if (rWords[index] == 0)
                {
                    ++index;
                    continue;
                }

                ulong r1r2 = GetDoubleWord(rWords, index, rSize);
                if (r1r2 == b1b2)
                {
                    // Iterate through the rest of b comparing words of r and b until they are not equal
                    int bIndex = 2;
                    int rIndex = index + 2;
                    FindNextNonEqual(rWords, bWords, bSize, ref rIndex, ref bIndex);
                    if (rIndex < rSize && bIndex < bSize && rWords[rIndex] < bWords[bIndex])
                    {
                        if (index + 1 > rSize)
                        {
                            break;
                        }
                        InternalDivide(rWords, rSize, r1r2 / b1plus1, bWords, bSize, index, cWords);
                    }
                    else
                    {
                        // Quotient is 1, just subtract b from r
                        SubtractInPlace(rWords, bWords, bSize, index);
                        cWords[index - 1]++;
                        Carry(cWords, index);
                    }
                }
                else if (r1r2 >= b1b2plus1)
                {
                    InternalDivide(rWords, rSize, r1r2 / b1b2plus1, bWords, bSize, index - 1, cWords);
                }
                else
                {
                    InternalDivide(rWords, rSize, r1r2 / b1plus1, bWords, bSize, index, cWords);
                }
            }
            c = Normalize(c, out cOffset);
            r = Normalize(r, out rOffset);
            // We added artificially an extra word onto r and c to cope with carry overflow (so take away again now)
            cOffset += BASE_FIG;
            rOffset += BASE_FIG;
            return c;
        }
        public static Fraction/*!*/ Multiply(Fraction/*!*/ x, Fraction/*!*/ y, out int offset)
        {
            int xPrecision = x.Precision;
            int yPrecision = y.Precision;
            int zPrecision = xPrecision + yPrecision;
            uint[] xData = x._words;
            uint[] yData = y._words;
            uint[] zData = new uint[zPrecision];
            Fraction z = new Fraction(zData);

            for (int xIndex = xPrecision - 1; xIndex >= 0; xIndex--)
            {
                uint xValue = xData[xIndex];
                int zIndex = zPrecision - (xPrecision - xIndex);
                ulong carry = 0;
                for (int yIndex = yPrecision - 1; yIndex >= 0; yIndex--)
                {
                    carry = carry + ((ulong)xValue) * yData[yIndex] + zData[zIndex];
                    zData[zIndex--] = (uint)(carry % BASE);
                    carry /= BASE;
                }
                while (carry != 0)
                {
                    carry += zData[zIndex];
                    zData[zIndex--] = (uint)carry;
                    carry /= BASE;
                }
            }
            z = Fraction.Normalize(z, out offset);
            return z;
        }
        public static Fraction/*!*/ Subtract(Fraction/*!*/ x, Fraction/*!*/ y, int exponentDiff, out int exponentOffset, out int sign)
        {
            Fraction upper = x;
            Fraction lower = y;

            sign = Compare(x, y, exponentDiff);
            if (sign == 0)
            {
                exponentOffset = 0;
                return new Fraction(1);
            }
            else if (sign < 0)
            {
                exponentDiff = -exponentDiff;
                upper = y;
                lower = x;
            }

            // Calculate the word and digit offsets between upper and lower
            int wordOffset = exponentDiff / BASE_FIG;
            int digitOffset = exponentDiff % BASE_FIG;

            // If necessary we need to scroll one of the arrays
            if (digitOffset != 0)
            {
                lower = ScrollRight(lower, digitOffset);
            }

            int lowerStart = wordOffset;

            // We should now have something like:
            // UUU UUU UU0 000 000 000 (upperStart=0, upperLength=8)
            // 000 0LL LLL LLL LLL LL0 (lowerStart=4, lowerLength=13)
            // assuming that exponentDiff is 4 (or -4) and BASE_FIG is 3
            // where each character above is a decimal digit and a space indicates a word boundary
            // Also, upper should be larger than lower

            int zPrecision = System.Math.Max(upper.Precision, lower.Precision + lowerStart);
            Fraction z = new Fraction(zPrecision);

            uint[] upperWords = upper._words;
            uint[] lowerWords = lower._words;
            uint[] zWords = z._words;

            // Copy words of upper straight into z
            Array.Copy(upperWords, 0, zWords, 0, upper.Precision);

            //// Subtract words of lower from z, borrowing as necessary
            SubtractInPlace(zWords, lowerWords, lower.Precision, lowerStart);

            z = Fraction.Normalize(z, out exponentOffset);
            return z;
        }
Beispiel #10
0
        public static Fraction/*!*/ Add(Fraction/*!*/ x, Fraction/*!*/y, int exponentDiff, out int exponentOffset)
        {
            Fraction upper = x;
            Fraction lower = y;

            // Switch x and y around if the exponentDiff is negative
            bool swap = exponentDiff < 0;
            if (swap)
            {
                exponentDiff = -exponentDiff;
                upper = y;
                lower = x;
            }

            // Calculate the word and digit offsets between upper and lower
            int wordOffset = exponentDiff / BASE_FIG;
            int digitOffset = exponentDiff % BASE_FIG;

            // If necessary we need to scroll one of the arrays
            if (digitOffset != 0)
            {
                lower = ScrollRight(lower, digitOffset);
            }

            int upperStart = 0;
            int lowerStart = wordOffset;

            // We should now have something like:
            // UUU UUU UU0 000 000 000 (upperStart=0, upperLength=8)
            // 000 0LL LLL LLL LLL LL0 (lowerStart=4, lowerLength=13)
            // assuming that exponentDiff is 4 (or -4) and BASE_FIG is 3
            // where each character above is a decimal digit and a space indicates a word boundary

            int zPrecision = System.Math.Max(upper.Precision, lower.Precision + wordOffset) + 1;
            Fraction z = new Fraction(zPrecision);

            uint[] upperWords = upper._words;
            uint[] lowerWords = lower._words;
            uint[] zWords = z._words;

            // Copy words of lower straight into z
            Array.Copy(lowerWords, 0, zWords, lowerStart + 1, lower.Precision);

            // Add words of upper into z, carrying as necessary
            ulong carry = 0;
            for (int i = upper.Precision - 1; i >= upperStart; i--)
            {
                carry += upperWords[i] + zWords[i + 1];
                zWords[i + 1] = (uint)(carry % BASE);
                carry /= BASE;
            }
            Debug.Assert(carry / BASE == 0);
            zWords[0] = (uint)(carry % BASE);

            // We expect there to be BASE_FIG offset when normalizing unless
            // the carry overflowed into the top word.
            z = Fraction.Normalize(z, out exponentOffset);
            exponentOffset += BASE_FIG;
            return z;
        }