PageRenderTime 39ms CodeModel.GetById 3ms app.highlight 28ms RepoModel.GetById 2ms app.codeStats 0ms

/IronPython_Main/Languages/Ruby/Libraries.LCA_RESTRICTED/BigDecimal/Fraction.cs

#
C# | 778 lines | 562 code | 94 blank | 122 comment | 133 complexity | 4be9a9dd8017145fa9a7a5458cb746b9 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Apache License, Version 2.0, please send an email to 
  8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Apache License, Version 2.0.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16using System;
 17using System.Diagnostics;
 18using System.Text;
 19using Microsoft.Scripting.Math;
 20using System.Globalization;
 21
 22namespace IronRuby.StandardLibrary.BigDecimal {
 23    public class Fraction {
 24
 25        #region Constants
 26        // Number of decimal places held in each word [BASE_FIG == log10(BASE) and 10^BASE_FIG == BASE]
 27        public const int BASE_FIG = 9;
 28        // Each word contains a value between 0 and BASE-1
 29        public const uint BASE = 1000000000;
 30        // Half the BASE value used in rounding
 31        public const uint HALF_BASE = BASE / 2;
 32        // BASE divided by 10
 33        public const uint BASE_DIV_10 = BASE / 10;
 34        #endregion
 35
 36        #region Special Values
 37        public static readonly Fraction Zero = new Fraction(new uint[] { 0 });
 38        public static readonly Fraction One = new Fraction(new uint[] { BASE_DIV_10 });
 39        public static readonly Fraction Five = new Fraction(new uint[] { 5 * BASE_DIV_10 });
 40        #endregion
 41
 42        #region Private Fields
 43        // The digits of the fraction held in words.
 44        // Each word holds BASE_FIG decimal digits.
 45        // The words are stored in most significant word first
 46        // e.g 0.1234567890123456789 will be stored as new uint[] { 123456789, 012345678, 900000000 }
 47        private uint[] _words;
 48        // The current number of elements in use in the _words array
 49        private int _precision;
 50        private readonly static uint[] _powers;
 51        static Fraction() {
 52            // Generate the powers array - should look like [BASE/10, BASE/100, ... , 1]
 53            _powers = new uint[BASE_FIG];
 54            int i = 0;
 55            for (uint p = BASE/10; p > 0; p /= 10 ) {
 56                _powers[i] = p;
 57                ++i;
 58            }
 59        }
 60        #endregion
 61
 62        #region Creation
 63        public Fraction(uint[] data) {
 64            this._words = data;
 65            this.Precision = MaxPrecision;
 66        }
 67
 68        public Fraction(int maxPrecision)
 69            : this(maxPrecision, maxPrecision) {
 70        }
 71
 72        public Fraction(Fraction/*!*/ copyFrom, int maxPrecision)
 73            : this(maxPrecision, maxPrecision) {
 74            // Copy the fraction
 75            int length = Math.Min(maxPrecision, copyFrom.MaxPrecision);
 76            Array.Copy(copyFrom._words, _words, length);
 77        }
 78
 79        public Fraction(int maxPrecision, int precision) {
 80            if (maxPrecision <= 0) {
 81                throw new ArgumentException("maxPrecision must be greater than zero");
 82            }
 83            _words = new uint[maxPrecision];
 84            Precision = precision;
 85        }
 86        public static Fraction/*!*/ Create(string/*!*/ digits) {
 87            int digitOffset;
 88            return Parse(digits, out digitOffset);
 89        }
 90        #endregion
 91
 92        #region Public Properties
 93        public int Precision {
 94            get { return _precision; }
 95            set {
 96                if (value < 1 || value > _words.Length) {
 97                    throw new ArgumentException("Precision must be in [1,MaxPrecision]");
 98                }
 99                _precision = value;
100            }
101        }
102        public int MaxPrecision { get { return _words.Length; } }
103
104        public int DigitCount {
105            get {
106                // Find last non-zero word
107                int wordIndex = Precision-1;
108                while (wordIndex > 0 && _words[wordIndex] == 0) {
109                    --wordIndex;
110                }
111
112                // Estimate digit count
113                int digits = (wordIndex+1) * BASE_FIG;
114
115                // Subtract trailing zeros count from digits in last word
116                uint lastWord = _words[wordIndex];
117                uint factor = 10;
118                while (factor <= BASE && (lastWord % factor) == 0) {
119                    --digits;
120                    factor = factor * 10;
121                }
122                return digits;
123            }
124        }
125
126        public bool IsOne {
127            get { return Precision == 1 && _words[0] == BASE_DIV_10; }
128        }
129        public bool IsZero {
130            get { return Precision == 1 && _words[0] == 0; }
131        }
132        #endregion
133
134        #region Public Methods
135        public override string ToString() {
136            StringBuilder sb = new StringBuilder(Precision * BASE_FIG);
137            for (int wordIndex = 0; wordIndex < Precision; wordIndex++) {
138                uint word = _words[wordIndex];
139                sb.AppendFormat("{0:D"+BASE_FIG+"}", word);
140            }
141            // Trim off last zeros
142            string str = sb.ToString().TrimEnd('0');
143
144            // Special case for zero
145            if (str.Length == 0) {
146                str = "0";
147            }
148 
149            return str;
150        }
151
152        public override int GetHashCode() {
153            int hash = 0;
154            for(int i=0; i<Precision; ++i) {
155                int word = (int)_words[i];
156                hash = (31 * hash + word) ^ word;
157            }
158            return hash;
159        }
160
161        public static Fraction/*!*/ Add(Fraction/*!*/ x, Fraction/*!*/y, int exponentDiff, out int exponentOffset) {
162            Fraction upper = x;
163            Fraction lower = y;
164
165            // Switch x and y around if the exponentDiff is negative
166            bool swap = exponentDiff < 0;
167            if (swap) {
168                exponentDiff = -exponentDiff;
169                upper = y;
170                lower = x;
171            }
172
173            // Calculate the word and digit offsets between upper and lower
174            int wordOffset = exponentDiff / BASE_FIG;
175            int digitOffset = exponentDiff % BASE_FIG;
176
177            // If necessary we need to scroll one of the arrays
178            if (digitOffset != 0) {
179                lower = ScrollRight(lower, digitOffset);
180            }
181
182            int upperStart = 0;
183            int lowerStart = wordOffset;
184
185            // We should now have something like:
186            // UUU UUU UU0 000 000 000 (upperStart=0, upperLength=8)
187            // 000 0LL LLL LLL LLL LL0 (lowerStart=4, lowerLength=13)
188            // assuming that exponentDiff is 4 (or -4) and BASE_FIG is 3
189            // where each character above is a decimal digit and a space indicates a word boundary
190
191            int zPrecision = Math.Max(upper.Precision, lower.Precision+wordOffset) + 1;
192            Fraction z = new Fraction(zPrecision);
193
194            uint[] upperWords = upper._words;
195            uint[] lowerWords = lower._words;
196            uint[] zWords = z._words;
197
198            // Copy words of lower straight into z
199            Array.Copy(lowerWords, 0, zWords, lowerStart+1, lower.Precision);
200
201            // Add words of upper into z, carrying as necessary
202            ulong carry = 0;
203            for (int i = upper.Precision - 1; i >= upperStart; i--) {
204                carry += upperWords[i] + zWords[i+1];
205                zWords[i+1] = (uint)(carry % BASE);
206                carry /= BASE;
207            }
208            Debug.Assert(carry / BASE == 0);
209            zWords[0] = (uint)(carry % BASE);
210
211            // We expect there to be BASE_FIG offset when normalizing unless
212            // the carry overflowed into the top word.
213            z = Fraction.Normalize(z, out exponentOffset);
214            exponentOffset += BASE_FIG;
215            return z;
216        }
217
218        public static Fraction/*!*/ Subtract(Fraction/*!*/ x, Fraction/*!*/ y, int exponentDiff, out int exponentOffset, out int sign) {
219            Fraction upper = x;
220            Fraction lower = y;
221
222            sign = Compare(x, y, exponentDiff);
223            if (sign== 0) {
224                exponentOffset = 0;
225                return new Fraction(1);
226            } else if (sign < 0) {
227                exponentDiff = -exponentDiff;
228                upper = y;
229                lower = x;
230            }
231
232            // Calculate the word and digit offsets between upper and lower
233            int wordOffset = exponentDiff / BASE_FIG;
234            int digitOffset = exponentDiff % BASE_FIG;
235
236            // If necessary we need to scroll one of the arrays
237            if (digitOffset != 0) {
238                lower = ScrollRight(lower, digitOffset);
239            }
240
241            int lowerStart = wordOffset;
242
243            // We should now have something like:
244            // UUU UUU UU0 000 000 000 (upperStart=0, upperLength=8)
245            // 000 0LL LLL LLL LLL LL0 (lowerStart=4, lowerLength=13)
246            // assuming that exponentDiff is 4 (or -4) and BASE_FIG is 3
247            // where each character above is a decimal digit and a space indicates a word boundary
248            // Also, upper should be larger than lower
249
250            int zPrecision = Math.Max(upper.Precision, lower.Precision + lowerStart);
251            Fraction z = new Fraction(zPrecision);
252
253            uint[] upperWords = upper._words;
254            uint[] lowerWords = lower._words;
255            uint[] zWords = z._words;
256
257            // Copy words of upper straight into z
258            Array.Copy(upperWords, 0, zWords, 0, upper.Precision);
259
260            //// Subtract words of lower from z, borrowing as necessary
261            SubtractInPlace(zWords, lowerWords, lower.Precision, lowerStart);
262
263            z = Fraction.Normalize(z, out exponentOffset);
264            return z;
265        }
266
267        public static Fraction/*!*/ Multiply(Fraction/*!*/ x, Fraction/*!*/ y, out int offset) {
268            int xPrecision = x.Precision;
269            int yPrecision = y.Precision;
270            int zPrecision = xPrecision + yPrecision;
271            uint[] xData = x._words;
272            uint[] yData = y._words;
273            uint[] zData = new uint[zPrecision];
274            Fraction z = new Fraction(zData);
275
276            for (int xIndex = xPrecision-1; xIndex >= 0; xIndex--) {
277                uint xValue = xData[xIndex];
278                int zIndex = zPrecision - (xPrecision - xIndex);
279                ulong carry = 0;
280                for (int yIndex = yPrecision-1; yIndex >= 0; yIndex--) {
281                    carry = carry + ((ulong)xValue) * yData[yIndex] + zData[zIndex];
282                    zData[zIndex--] = (uint)(carry%BASE);
283                    carry /= BASE;
284                }
285                while (carry != 0) {
286                    carry += zData[zIndex];
287                    zData[zIndex--] = (uint)carry;
288                    carry /= BASE;
289                }
290            }
291            z = Fraction.Normalize(z, out offset);
292            return z;
293        }
294
295        public static Fraction/*!*/ Divide(Fraction/*!*/ a, Fraction/*!*/ b, int minPrecision, out Fraction/*!*/ r, out int cOffset, out int rOffset) {
296            int precision = Math.Max(a.MaxPrecision + b.MaxPrecision+1, minPrecision);
297            Fraction c = new Fraction(precision);
298            r = new Fraction(precision * 2);
299
300            uint[] aWords = a._words;
301            uint[] bWords = b._words;
302            uint[] cWords = c._words;
303            uint[] rWords = r._words;
304
305            int aSize = a.Precision;
306            int bSize = b.Precision;
307            int cSize = c.MaxPrecision;
308            int rSize = r.MaxPrecision;
309
310            // Initialize remainder (we add an extra word at the front to catch the overflow
311            Array.Copy(aWords, 0, rWords, 1, Math.Min(aSize, rSize-1));
312
313            // Setup basic values
314            ulong b1        = bWords[0];
315            ulong b1plus1   = bSize <= 1 ? b1               : b1 + 1;
316            ulong b1b2      = bSize <= 1 ? (ulong)bWords[0] * BASE : GetDoubleWord(bWords, 0, b.Precision);
317            ulong b1b2plus1 = bSize <= 2 ? b1b2             : b1b2 + 1;
318
319            // Main loop
320            int index = 1;
321            int size = Math.Min(cSize, rSize);
322            while (index < size) {
323                if (rWords[index] == 0) {
324                    ++index;
325                    continue;
326                }
327
328                ulong r1r2 = GetDoubleWord(rWords, index, rSize);
329                if (r1r2 == b1b2) {
330                    // Iterate through the rest of b comparing words of r and b until they are not equal
331                    int bIndex = 2;
332                    int rIndex = index + 2;
333                    FindNextNonEqual(rWords, bWords, bSize, ref rIndex, ref bIndex);
334                    if (rIndex < rSize && bIndex < bSize && rWords[rIndex] < bWords[bIndex]) {
335                        if (index + 1 > rSize) {
336                            break;
337                        }
338                        InternalDivide(rWords, rSize, r1r2 / b1plus1, bWords, bSize, index, cWords);
339                    } else {
340                        // Quotient is 1, just subtract b from r
341                        SubtractInPlace(rWords, bWords, bSize, index);
342                        cWords[index-1]++;
343                        Carry(cWords, index);
344                    }
345                } else if (r1r2 >= b1b2plus1) {
346                    InternalDivide(rWords, rSize, r1r2 / b1b2plus1, bWords, bSize, index - 1, cWords);
347                } else {
348                    InternalDivide(rWords, rSize, r1r2 / b1plus1, bWords, bSize, index, cWords);
349                }
350            }
351            c = Normalize(c, out cOffset);
352            r = Normalize(r, out rOffset);
353            // We added artificially an extra word onto r and c to cope with carry overflow (so take away again now)
354            cOffset += BASE_FIG;
355            rOffset += BASE_FIG;
356            return c;
357        }
358
359        public static int Compare(Fraction/*!*/ x, Fraction/*!*/ y, int exponentDiff) {
360            if (exponentDiff != 0) {
361                return exponentDiff > 0 ? 1 : -1;
362            }
363            int wordIndex = 0;
364            while (wordIndex < x.Precision && wordIndex < y.Precision) {
365                if (x._words[wordIndex] != y._words[wordIndex]) {
366                    return x._words[wordIndex] > y._words[wordIndex] ? 1 : -1;
367                }
368                wordIndex++;
369            }
370            if (wordIndex == x.Precision) {
371                while (wordIndex < y.Precision) {
372                    if (y._words[wordIndex] != 0) {
373                        return -1;
374                    }
375                    wordIndex++;
376                }
377            } else {
378                while (wordIndex < x.Precision) {
379                    if (x._words[wordIndex] != 0) {
380                        return 1;
381                    }
382                    wordIndex++;
383                }
384            }
385            return 0;
386        }
387
388#if SILVERLIGHT
389        public static int DivRem(int a, int b, out int result) {
390            result = a % b;
391            return (a / b);
392        }
393#endif
394
395        /// <summary>
396        /// Limits the precision of the given Fraction.
397        /// </summary>
398        /// <param name="sign">The sign of the BigDecimal</param>
399        /// <param name="fraction">The fraction to limit</param>
400        /// <param name="digits">The number of digits to which we are limiting</param>
401        /// <param name="mode">The rounding mode to use when limiting</param>
402        /// <returns>A new fraction that has no more than <paramref name="digits"/> digits.</returns>
403        /// <example>
404        /// Consider a fraction of 123456789 using default HalfUp rounding.
405        /// Limit : Result
406        /// 1       1
407        /// 2       12
408        /// 3       123
409        /// 4       1234
410        /// 5       12346
411        /// 6       123457
412        /// 7       1234568
413        /// 8       12345679
414        /// 9       123456789
415        /// 10      123456789
416        /// </example>
417        public static Fraction/*!*/ LimitPrecision(int sign, Fraction/*!*/ fraction, int digits, BigDecimal.RoundingModes mode, out int offset) {
418            Fraction result;
419
420            if (digits <= 0) {
421                uint digit = (uint)(fraction.IsZero ? 0 : 1);
422                if (RoundDigit(sign, digit, 0, mode) > 0) {
423                    offset = 1-digits;
424                    return One;
425                } else {
426                    offset = 0;
427                    return Zero;
428                }
429            }
430
431            if (digits >= fraction.DigitCount) {
432                offset = 0;
433                return fraction;
434            }
435
436            // Calculate offsets of relevant digits
437            int secondLastDigitIndex; // i.e. fraction[digits-1]
438            int secondLastWordIndex;
439            uint secondLastDigit;
440            int lastDigitIndex; // i.e. fraction[digits]
441            int lastWordIndex;
442            uint lastDigit;
443
444#if SILVERLIGHT
445            secondLastWordIndex = DivRem(digits - 1, BASE_FIG, out secondLastDigitIndex);
446#else
447            secondLastWordIndex = Math.DivRem(digits - 1, BASE_FIG, out secondLastDigitIndex);
448#endif
449            if (secondLastDigitIndex == BASE_FIG-1) {
450                lastWordIndex = secondLastWordIndex+1;
451                lastDigitIndex = 0;
452            } else {
453                lastWordIndex = secondLastWordIndex;
454                lastDigitIndex = secondLastDigitIndex + 1;
455            }
456
457            // TODO: Extract these calculations out into static readonly arrays
458            // Mask for last digit.  E.g. lastDigitIndex = 3, BASE_FIG = 9 => lastFactor = 1000000
459            uint lastFactor=_powers[lastDigitIndex];
460            // Mask for second last digit
461            uint secondLastFactor= _powers[secondLastDigitIndex];
462
463            // Calculate the current digits and rounding direction
464            secondLastDigit = (fraction._words[secondLastWordIndex] / secondLastFactor) % 10;
465            if (lastWordIndex < fraction.MaxPrecision) {
466                lastDigit = (fraction._words[lastWordIndex] / lastFactor) % 10;
467            } else {
468                lastDigit = 0;
469            }
470            int round = RoundDigit(sign, lastDigit, secondLastDigit, mode);
471
472            // Create a temporary fraction used to cause the rounding in the original
473            result = new Fraction(lastWordIndex+1);
474            Array.Copy(fraction._words, 0, result._words, 0, Math.Min(lastWordIndex+1, fraction.Precision));
475            // Clear the digits beyond the second last digit
476            result._words[secondLastWordIndex] = result._words[secondLastWordIndex] - (fraction._words[secondLastWordIndex] % secondLastFactor);
477            if (round > 0) {
478                // Increment the last digit of result by 1
479                Fraction temp = new Fraction(secondLastWordIndex + 1);
480                temp._words[secondLastWordIndex] = secondLastFactor;
481                result = Fraction.Add(result, temp, 0, out offset);
482            } else {
483                offset = 0;
484            }
485
486            result.Precision = Math.Min(secondLastWordIndex+1, result.MaxPrecision);
487            return result;
488        }
489        #endregion
490
491        #region Private Helpers
492        private static int RoundDigit(int sign, uint lastDigit, uint secondLastDigit, BigDecimal.RoundingModes roundingMode) {
493            int result = -1;
494            switch (roundingMode) {
495                case BigDecimal.RoundingModes.Up:
496                    if (lastDigit != 0) {
497                        result = 1;
498                    }
499                    break;
500                case BigDecimal.RoundingModes.HalfUp:
501                    if (lastDigit >= 5) {
502                        result = 1;
503                    }
504                    break;
505                case BigDecimal.RoundingModes.HalfDown:
506                    if (lastDigit > 5) {
507                        result = 1;
508                    }
509                    break;
510                case BigDecimal.RoundingModes.HalfEven:
511                    if (lastDigit > 5) {
512                        result = 1;
513                    } else if ((lastDigit == 5) && (secondLastDigit % 2 != 0)) {
514                        result = 1;
515                    }
516                    break;
517                case BigDecimal.RoundingModes.Ceiling:
518                    if (sign == 1 && lastDigit != 0) {
519                        result = 1;
520                    }
521                    break;
522                case BigDecimal.RoundingModes.Floor:
523                    if (sign == -1 && lastDigit != 0) {
524                        result = 1;
525                    }
526                    break;
527            }
528            return result;
529        }
530
531        /// <summary>
532        /// Divide r[index, ...] by q
533        /// </summary>
534        private static void InternalDivide(uint[] rWords, int rSize, ulong q, uint[] bWords, int bSize, int index, uint[] cWords) {
535            cWords[index] += (uint)q;
536            SubtractMultiple(rWords, rSize, q, bWords, bSize, index, bSize+index);
537        }
538
539        // r = r - q * b
540        private static void SubtractMultiple(uint[] rWords, int rSize, ulong q, uint[] bWords, int bSize, int index, int rIndex) {
541            int bIndex = bSize - 1;
542            uint borrow1 = 0;
543            uint borrow2 = 0;
544
545            while (bIndex >= 0) {
546                ulong qb = q * bWords[bIndex];
547                if (qb < BASE) {
548                    borrow1 = 0;
549                } else {
550                    borrow1 = (uint)(qb / BASE);
551                    qb -= (ulong)borrow1 * BASE;
552                }
553
554                if (rWords[rIndex] < qb) {
555                    rWords[rIndex] += (uint)(BASE - qb);
556                    borrow2 += borrow1 + 1;
557                } else{ 
558				    rWords[rIndex] -= (uint)qb;
559                    borrow2 += borrow1;
560                }
561
562                if (borrow2 != 0) {
563                    if (rWords[rIndex - 1] < borrow2) {
564                        rWords[rIndex - 1] += (BASE - borrow2);
565                        borrow2 = 1;
566                    } else {
567                        rWords[rIndex - 1] -= borrow2;
568                        borrow2 = 0;
569                    }
570                }
571
572                --rIndex;
573                --bIndex;
574            }
575        }
576
577        private static void FindNextNonEqual(uint[] i, uint[] j, int iSize, ref int iIndex, ref int jIndex) {
578            while (iIndex < iSize) {
579                if (i[iIndex] != j[jIndex]) {
580                    break;
581                }
582                ++iIndex;
583                ++jIndex;
584            }
585        }
586
587        /// <summary>
588        /// Subtract y from x in place.
589        /// </summary>
590        /// <param name="x"></param>
591        /// <param name="y"></param>
592        private static void SubtractInPlace(uint[] x, uint[] y, int count, int offset) {
593            uint borrow = 0;
594            int yIndex = count - 1;
595            int xIndex = offset + yIndex;
596			while(yIndex >= 0) {
597				if(x[xIndex] < y[yIndex] + borrow) {
598					x[xIndex] = x[xIndex] + (BASE -(y[yIndex] + borrow));
599					borrow = 1;
600                } else {
601                    x[xIndex] = x[xIndex] - (y[yIndex] + borrow);
602					borrow = 0;
603                }
604				--yIndex;
605				--xIndex;
606            }
607            // Continue to borrow as necessary
608            while (borrow != 0) {
609				if(x[xIndex] < borrow) {
610					x[xIndex] = x[xIndex] + (BASE - borrow);
611					borrow = 1;
612                } else {
613                    x[xIndex] = x[xIndex] - borrow;
614					borrow = 0;
615                }
616				--xIndex;
617            }
618        }
619
620        private static ulong GetDoubleWord(uint[] a, int i, int precision) {
621            if (i+1 == precision) {
622                return (ulong)a[i] * BASE;
623            } else {
624                return (ulong)a[i] * BASE + a[i + 1];
625            }
626        }
627
628        private static void Carry(uint[] a, int i) {
629            while (a[i] >= BASE) {
630                a[i] -= BASE;
631                --i;
632                ++a[i];
633            }
634        }
635
636        // Note: Scrolling left is just the same as scrolling right by (BASE_FIG-offset)
637        private static Fraction/*!*/ ScrollRight(Fraction/*!*/ fraction, int offset) {
638            Debug.Assert(offset <= BASE_FIG);
639            Debug.Assert(offset >= 0);
640
641            // Don't do anything if offset is not going to change the internal digit layout
642            if (offset % BASE_FIG == 0) {
643                return fraction;
644            }
645
646            int oldPrecision = fraction.Precision;
647            int newPrecision = oldPrecision + 1;
648
649            Fraction newFraction = new Fraction(newPrecision);
650
651            // Calculate masking values
652            // divisor is used to mask off and bring top digits down to bottom digits (TTTTTTBBBB / divisor == TTTTTT)
653            // also divisor is used to mask off the bottom digits (TTTTTTBBBB % divisor == BBBB)
654            // factor is then used to bring bottom digits up to top digits (BBBB * factor == BBBB000000)
655            uint downFactor = 1;
656            for (int i = 0; i < offset; ++i ) {
657                downFactor *= 10;
658            }
659            uint upFactor = BASE / downFactor;
660
661            // Scroll the digits
662            // We want to go from TTTTTTBBBB TTTTTTBBBB TTTTTTBBBB to 0000TTTTTT BBBBTTTTTT BBBBTTTTTT BBBB000000
663            // I.E. We are pushing all the digits to the right by "offset" amount and padding with zeros
664            uint topDigits = 0;
665            uint bottomDigits = 0;
666            int wordIndex = 0;
667            while(wordIndex < oldPrecision) {
668                topDigits = fraction._words[wordIndex] / downFactor;
669                newFraction._words[wordIndex] = bottomDigits + topDigits;
670                bottomDigits = (fraction._words[wordIndex] % downFactor) * upFactor;
671                wordIndex++;
672            }
673
674            // Fix up the last word
675            newFraction._words[wordIndex] = bottomDigits;
676
677            return newFraction;
678        }
679
680         private static Fraction/*!*/ Parse(string/*!*/ digits, out int digitOffset) {
681            // Trim off any trailing zeros
682            digits = digits.TrimEnd('0');
683
684            if (digits == "") {
685                digitOffset = 0;
686                return Zero;
687            }
688
689            // Create the new fraction
690            int precision = digits.Length / BASE_FIG;
691            int finalDigits = digits.Length % BASE_FIG;
692            if (finalDigits > 0) { ++precision; }
693            Fraction/*!*/ fraction = new Fraction(precision);
694
695            // Iterate through groups of BASE_FIG digits
696            int digitIndex;
697            int wordIndex = 0;
698            for (digitIndex = 0; digitIndex+BASE_FIG <= digits.Length; digitIndex+=BASE_FIG) {
699                fraction._words[wordIndex] = uint.Parse(digits.Substring(digitIndex, BASE_FIG), CultureInfo.InvariantCulture);
700                wordIndex++;
701            }
702
703            // Add on the last few digits, adding extra zeros as necessary
704            if (finalDigits > 0) {
705                uint lastWord = uint.Parse(digits.Substring(digitIndex), CultureInfo.InvariantCulture);
706                while (finalDigits < BASE_FIG) {
707                    lastWord *= 10;
708                    ++finalDigits;
709                }
710                fraction._words[wordIndex] = lastWord;
711            }
712
713            fraction = Fraction.Normalize(fraction, out digitOffset);
714            return fraction;
715        }
716
717        private static Fraction/*!*/ Normalize(Fraction/*!*/ f, out int digitOffset) {
718            // Count leading zero words
719            int leadingZeroWords = 0;
720            while (leadingZeroWords < f.MaxPrecision && f._words[leadingZeroWords] == 0) {
721                leadingZeroWords++;
722            }
723            
724            if (leadingZeroWords == f.MaxPrecision) {
725                // The fraction is just all zeros
726                digitOffset = 0;
727                return f;
728            }
729
730            // Count trailing zero words
731            int trailingZeroWords = 0;
732            while (f._words[f.Precision - 1 - trailingZeroWords] == 0) {
733                trailingZeroWords++;
734            }
735            // Count leading zero digits (within the first non-zero word)
736            // and also calculate up and down factors for masking
737            int leadingZeroDigits = BASE_FIG;
738            uint firstWord = f._words[leadingZeroWords];
739            uint downFactor = 1;
740            while (firstWord != 0) {
741                firstWord /= 10;
742                leadingZeroDigits--;
743                downFactor *= 10;
744            }
745            uint upFactor = BASE / downFactor;
746
747            int newPrecision = f.Precision - leadingZeroWords - trailingZeroWords;
748            Fraction n = new Fraction(newPrecision);
749            // Copy the non-zero words across
750            Array.Copy(f._words, leadingZeroWords, n._words, 0, n.MaxPrecision);
751
752            if (leadingZeroDigits > 0) {
753                // Scroll the digits within the non-zero words to trim off the first zero digits
754                uint bottomDigits = n._words[0] * upFactor;
755                uint topDigits = 0;
756                int wordIndex = 1;
757                while (wordIndex < n.Precision) {
758                    topDigits = n._words[wordIndex] / downFactor;
759                    n._words[wordIndex - 1] = bottomDigits + topDigits;
760                    bottomDigits = (n._words[wordIndex] % downFactor) * upFactor;
761                    wordIndex++;
762                }
763
764                // Fix up the last word
765                n._words[wordIndex-1] = bottomDigits;
766                // and the Precision
767                n.Precision = wordIndex;
768            }
769
770
771            // Return the offset in digits caused by the normalization.
772            digitOffset = -(leadingZeroWords * BASE_FIG + leadingZeroDigits);
773            return n;
774        }
775
776       #endregion
777    }
778}