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