fraction.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. /**
  2. * @license Fraction.js v4.3.7 31/08/2023
  3. * https://www.xarg.org/2014/03/rational-numbers-in-javascript/
  4. *
  5. * Copyright (c) 2023, Robert Eisele (robert@raw.org)
  6. * Dual licensed under the MIT or GPL Version 2 licenses.
  7. **/
  8. /**
  9. *
  10. * This class offers the possibility to calculate fractions.
  11. * You can pass a fraction in different formats. Either as array, as double, as string or as an integer.
  12. *
  13. * Array/Object form
  14. * [ 0 => <numerator>, 1 => <denominator> ]
  15. * [ n => <numerator>, d => <denominator> ]
  16. *
  17. * Integer form
  18. * - Single integer value
  19. *
  20. * Double form
  21. * - Single double value
  22. *
  23. * String form
  24. * 123.456 - a simple double
  25. * 123/456 - a string fraction
  26. * 123.'456' - a double with repeating decimal places
  27. * 123.(456) - synonym
  28. * 123.45'6' - a double with repeating last place
  29. * 123.45(6) - synonym
  30. *
  31. * Example:
  32. *
  33. * var f = new Fraction("9.4'31'");
  34. * f.mul([-4, 3]).div(4.9);
  35. *
  36. */
  37. // Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
  38. // Example: 1/7 = 0.(142857) has 6 repeating decimal places.
  39. // If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
  40. var MAX_CYCLE_LEN = 2000;
  41. // Parsed data to avoid calling "new" all the time
  42. var P = {
  43. "s": 1,
  44. "n": 0,
  45. "d": 1
  46. };
  47. function assign(n, s) {
  48. if (isNaN(n = parseInt(n, 10))) {
  49. throw InvalidParameter();
  50. }
  51. return n * s;
  52. }
  53. // Creates a new Fraction internally without the need of the bulky constructor
  54. function newFraction(n, d) {
  55. if (d === 0) {
  56. throw DivisionByZero();
  57. }
  58. var f = Object.create(Fraction.prototype);
  59. f["s"] = n < 0 ? -1 : 1;
  60. n = n < 0 ? -n : n;
  61. var a = gcd(n, d);
  62. f["n"] = n / a;
  63. f["d"] = d / a;
  64. return f;
  65. }
  66. function factorize(num) {
  67. var factors = {};
  68. var n = num;
  69. var i = 2;
  70. var s = 4;
  71. while (s <= n) {
  72. while (n % i === 0) {
  73. n/= i;
  74. factors[i] = (factors[i] || 0) + 1;
  75. }
  76. s+= 1 + 2 * i++;
  77. }
  78. if (n !== num) {
  79. if (n > 1)
  80. factors[n] = (factors[n] || 0) + 1;
  81. } else {
  82. factors[num] = (factors[num] || 0) + 1;
  83. }
  84. return factors;
  85. }
  86. var parse = function(p1, p2) {
  87. var n = 0, d = 1, s = 1;
  88. var v = 0, w = 0, x = 0, y = 1, z = 1;
  89. var A = 0, B = 1;
  90. var C = 1, D = 1;
  91. var N = 10000000;
  92. var M;
  93. if (p1 === undefined || p1 === null) {
  94. /* void */
  95. } else if (p2 !== undefined) {
  96. n = p1;
  97. d = p2;
  98. s = n * d;
  99. if (n % 1 !== 0 || d % 1 !== 0) {
  100. throw NonIntegerParameter();
  101. }
  102. } else
  103. switch (typeof p1) {
  104. case "object":
  105. {
  106. if ("d" in p1 && "n" in p1) {
  107. n = p1["n"];
  108. d = p1["d"];
  109. if ("s" in p1)
  110. n*= p1["s"];
  111. } else if (0 in p1) {
  112. n = p1[0];
  113. if (1 in p1)
  114. d = p1[1];
  115. } else {
  116. throw InvalidParameter();
  117. }
  118. s = n * d;
  119. break;
  120. }
  121. case "number":
  122. {
  123. if (p1 < 0) {
  124. s = p1;
  125. p1 = -p1;
  126. }
  127. if (p1 % 1 === 0) {
  128. n = p1;
  129. } else if (p1 > 0) { // check for != 0, scale would become NaN (log(0)), which converges really slow
  130. if (p1 >= 1) {
  131. z = Math.pow(10, Math.floor(1 + Math.log(p1) / Math.LN10));
  132. p1/= z;
  133. }
  134. // Using Farey Sequences
  135. // http://www.johndcook.com/blog/2010/10/20/best-rational-approximation/
  136. while (B <= N && D <= N) {
  137. M = (A + C) / (B + D);
  138. if (p1 === M) {
  139. if (B + D <= N) {
  140. n = A + C;
  141. d = B + D;
  142. } else if (D > B) {
  143. n = C;
  144. d = D;
  145. } else {
  146. n = A;
  147. d = B;
  148. }
  149. break;
  150. } else {
  151. if (p1 > M) {
  152. A+= C;
  153. B+= D;
  154. } else {
  155. C+= A;
  156. D+= B;
  157. }
  158. if (B > N) {
  159. n = C;
  160. d = D;
  161. } else {
  162. n = A;
  163. d = B;
  164. }
  165. }
  166. }
  167. n*= z;
  168. } else if (isNaN(p1) || isNaN(p2)) {
  169. d = n = NaN;
  170. }
  171. break;
  172. }
  173. case "string":
  174. {
  175. B = p1.match(/\d+|./g);
  176. if (B === null)
  177. throw InvalidParameter();
  178. if (B[A] === '-') {// Check for minus sign at the beginning
  179. s = -1;
  180. A++;
  181. } else if (B[A] === '+') {// Check for plus sign at the beginning
  182. A++;
  183. }
  184. if (B.length === A + 1) { // Check if it's just a simple number "1234"
  185. w = assign(B[A++], s);
  186. } else if (B[A + 1] === '.' || B[A] === '.') { // Check if it's a decimal number
  187. if (B[A] !== '.') { // Handle 0.5 and .5
  188. v = assign(B[A++], s);
  189. }
  190. A++;
  191. // Check for decimal places
  192. if (A + 1 === B.length || B[A + 1] === '(' && B[A + 3] === ')' || B[A + 1] === "'" && B[A + 3] === "'") {
  193. w = assign(B[A], s);
  194. y = Math.pow(10, B[A].length);
  195. A++;
  196. }
  197. // Check for repeating places
  198. if (B[A] === '(' && B[A + 2] === ')' || B[A] === "'" && B[A + 2] === "'") {
  199. x = assign(B[A + 1], s);
  200. z = Math.pow(10, B[A + 1].length) - 1;
  201. A+= 3;
  202. }
  203. } else if (B[A + 1] === '/' || B[A + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
  204. w = assign(B[A], s);
  205. y = assign(B[A + 2], 1);
  206. A+= 3;
  207. } else if (B[A + 3] === '/' && B[A + 1] === ' ') { // Check for a complex fraction "123 1/2"
  208. v = assign(B[A], s);
  209. w = assign(B[A + 2], s);
  210. y = assign(B[A + 4], 1);
  211. A+= 5;
  212. }
  213. if (B.length <= A) { // Check for more tokens on the stack
  214. d = y * z;
  215. s = /* void */
  216. n = x + d * v + z * w;
  217. break;
  218. }
  219. /* Fall through on error */
  220. }
  221. default:
  222. throw InvalidParameter();
  223. }
  224. if (d === 0) {
  225. throw DivisionByZero();
  226. }
  227. P["s"] = s < 0 ? -1 : 1;
  228. P["n"] = Math.abs(n);
  229. P["d"] = Math.abs(d);
  230. };
  231. function modpow(b, e, m) {
  232. var r = 1;
  233. for (; e > 0; b = (b * b) % m, e >>= 1) {
  234. if (e & 1) {
  235. r = (r * b) % m;
  236. }
  237. }
  238. return r;
  239. }
  240. function cycleLen(n, d) {
  241. for (; d % 2 === 0;
  242. d/= 2) {
  243. }
  244. for (; d % 5 === 0;
  245. d/= 5) {
  246. }
  247. if (d === 1) // Catch non-cyclic numbers
  248. return 0;
  249. // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
  250. // 10^(d-1) % d == 1
  251. // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
  252. // as we want to translate the numbers to strings.
  253. var rem = 10 % d;
  254. var t = 1;
  255. for (; rem !== 1; t++) {
  256. rem = rem * 10 % d;
  257. if (t > MAX_CYCLE_LEN)
  258. return 0; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
  259. }
  260. return t;
  261. }
  262. function cycleStart(n, d, len) {
  263. var rem1 = 1;
  264. var rem2 = modpow(10, len, d);
  265. for (var t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
  266. // Solve 10^s == 10^(s+t) (mod d)
  267. if (rem1 === rem2)
  268. return t;
  269. rem1 = rem1 * 10 % d;
  270. rem2 = rem2 * 10 % d;
  271. }
  272. return 0;
  273. }
  274. function gcd(a, b) {
  275. if (!a)
  276. return b;
  277. if (!b)
  278. return a;
  279. while (1) {
  280. a%= b;
  281. if (!a)
  282. return b;
  283. b%= a;
  284. if (!b)
  285. return a;
  286. }
  287. };
  288. /**
  289. * Module constructor
  290. *
  291. * @constructor
  292. * @param {number|Fraction=} a
  293. * @param {number=} b
  294. */
  295. export default function Fraction(a, b) {
  296. parse(a, b);
  297. if (this instanceof Fraction) {
  298. a = gcd(P["d"], P["n"]); // Abuse variable a
  299. this["s"] = P["s"];
  300. this["n"] = P["n"] / a;
  301. this["d"] = P["d"] / a;
  302. } else {
  303. return newFraction(P['s'] * P['n'], P['d']);
  304. }
  305. }
  306. var DivisionByZero = function() { return new Error("Division by Zero"); };
  307. var InvalidParameter = function() { return new Error("Invalid argument"); };
  308. var NonIntegerParameter = function() { return new Error("Parameters must be integer"); };
  309. Fraction.prototype = {
  310. "s": 1,
  311. "n": 0,
  312. "d": 1,
  313. /**
  314. * Calculates the absolute value
  315. *
  316. * Ex: new Fraction(-4).abs() => 4
  317. **/
  318. "abs": function() {
  319. return newFraction(this["n"], this["d"]);
  320. },
  321. /**
  322. * Inverts the sign of the current fraction
  323. *
  324. * Ex: new Fraction(-4).neg() => 4
  325. **/
  326. "neg": function() {
  327. return newFraction(-this["s"] * this["n"], this["d"]);
  328. },
  329. /**
  330. * Adds two rational numbers
  331. *
  332. * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
  333. **/
  334. "add": function(a, b) {
  335. parse(a, b);
  336. return newFraction(
  337. this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
  338. this["d"] * P["d"]
  339. );
  340. },
  341. /**
  342. * Subtracts two rational numbers
  343. *
  344. * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
  345. **/
  346. "sub": function(a, b) {
  347. parse(a, b);
  348. return newFraction(
  349. this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
  350. this["d"] * P["d"]
  351. );
  352. },
  353. /**
  354. * Multiplies two rational numbers
  355. *
  356. * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
  357. **/
  358. "mul": function(a, b) {
  359. parse(a, b);
  360. return newFraction(
  361. this["s"] * P["s"] * this["n"] * P["n"],
  362. this["d"] * P["d"]
  363. );
  364. },
  365. /**
  366. * Divides two rational numbers
  367. *
  368. * Ex: new Fraction("-17.(345)").inverse().div(3)
  369. **/
  370. "div": function(a, b) {
  371. parse(a, b);
  372. return newFraction(
  373. this["s"] * P["s"] * this["n"] * P["d"],
  374. this["d"] * P["n"]
  375. );
  376. },
  377. /**
  378. * Clones the actual object
  379. *
  380. * Ex: new Fraction("-17.(345)").clone()
  381. **/
  382. "clone": function() {
  383. return newFraction(this['s'] * this['n'], this['d']);
  384. },
  385. /**
  386. * Calculates the modulo of two rational numbers - a more precise fmod
  387. *
  388. * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
  389. **/
  390. "mod": function(a, b) {
  391. if (isNaN(this['n']) || isNaN(this['d'])) {
  392. return new Fraction(NaN);
  393. }
  394. if (a === undefined) {
  395. return newFraction(this["s"] * this["n"] % this["d"], 1);
  396. }
  397. parse(a, b);
  398. if (0 === P["n"] && 0 === this["d"]) {
  399. throw DivisionByZero();
  400. }
  401. /*
  402. * First silly attempt, kinda slow
  403. *
  404. return that["sub"]({
  405. "n": num["n"] * Math.floor((this.n / this.d) / (num.n / num.d)),
  406. "d": num["d"],
  407. "s": this["s"]
  408. });*/
  409. /*
  410. * New attempt: a1 / b1 = a2 / b2 * q + r
  411. * => b2 * a1 = a2 * b1 * q + b1 * b2 * r
  412. * => (b2 * a1 % a2 * b1) / (b1 * b2)
  413. */
  414. return newFraction(
  415. this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]),
  416. P["d"] * this["d"]
  417. );
  418. },
  419. /**
  420. * Calculates the fractional gcd of two rational numbers
  421. *
  422. * Ex: new Fraction(5,8).gcd(3,7) => 1/56
  423. */
  424. "gcd": function(a, b) {
  425. parse(a, b);
  426. // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
  427. return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]);
  428. },
  429. /**
  430. * Calculates the fractional lcm of two rational numbers
  431. *
  432. * Ex: new Fraction(5,8).lcm(3,7) => 15
  433. */
  434. "lcm": function(a, b) {
  435. parse(a, b);
  436. // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
  437. if (P["n"] === 0 && this["n"] === 0) {
  438. return newFraction(0, 1);
  439. }
  440. return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]));
  441. },
  442. /**
  443. * Calculates the ceil of a rational number
  444. *
  445. * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
  446. **/
  447. "ceil": function(places) {
  448. places = Math.pow(10, places || 0);
  449. if (isNaN(this["n"]) || isNaN(this["d"])) {
  450. return new Fraction(NaN);
  451. }
  452. return newFraction(Math.ceil(places * this["s"] * this["n"] / this["d"]), places);
  453. },
  454. /**
  455. * Calculates the floor of a rational number
  456. *
  457. * Ex: new Fraction('4.(3)').floor() => (4 / 1)
  458. **/
  459. "floor": function(places) {
  460. places = Math.pow(10, places || 0);
  461. if (isNaN(this["n"]) || isNaN(this["d"])) {
  462. return new Fraction(NaN);
  463. }
  464. return newFraction(Math.floor(places * this["s"] * this["n"] / this["d"]), places);
  465. },
  466. /**
  467. * Rounds a rational number
  468. *
  469. * Ex: new Fraction('4.(3)').round() => (4 / 1)
  470. **/
  471. "round": function(places) {
  472. places = Math.pow(10, places || 0);
  473. if (isNaN(this["n"]) || isNaN(this["d"])) {
  474. return new Fraction(NaN);
  475. }
  476. return newFraction(Math.round(places * this["s"] * this["n"] / this["d"]), places);
  477. },
  478. /**
  479. * Rounds a rational number to a multiple of another rational number
  480. *
  481. * Ex: new Fraction('0.9').roundTo("1/8") => 7 / 8
  482. **/
  483. "roundTo": function(a, b) {
  484. /*
  485. k * x/y ≤ a/b < (k+1) * x/y
  486. ⇔ k ≤ a/b / (x/y) < (k+1)
  487. ⇔ k = floor(a/b * y/x)
  488. */
  489. parse(a, b);
  490. return newFraction(this['s'] * Math.round(this['n'] * P['d'] / (this['d'] * P['n'])) * P['n'], P['d']);
  491. },
  492. /**
  493. * Gets the inverse of the fraction, means numerator and denominator are exchanged
  494. *
  495. * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
  496. **/
  497. "inverse": function() {
  498. return newFraction(this["s"] * this["d"], this["n"]);
  499. },
  500. /**
  501. * Calculates the fraction to some rational exponent, if possible
  502. *
  503. * Ex: new Fraction(-1,2).pow(-3) => -8
  504. */
  505. "pow": function(a, b) {
  506. parse(a, b);
  507. // Trivial case when exp is an integer
  508. if (P['d'] === 1) {
  509. if (P['s'] < 0) {
  510. return newFraction(Math.pow(this['s'] * this["d"], P['n']), Math.pow(this["n"], P['n']));
  511. } else {
  512. return newFraction(Math.pow(this['s'] * this["n"], P['n']), Math.pow(this["d"], P['n']));
  513. }
  514. }
  515. // Negative roots become complex
  516. // (-a/b)^(c/d) = x
  517. // <=> (-1)^(c/d) * (a/b)^(c/d) = x
  518. // <=> (cos(pi) + i*sin(pi))^(c/d) * (a/b)^(c/d) = x # rotate 1 by 180°
  519. // <=> (cos(c*pi/d) + i*sin(c*pi/d)) * (a/b)^(c/d) = x # DeMoivre's formula in Q ( https://proofwiki.org/wiki/De_Moivre%27s_Formula/Rational_Index )
  520. // From which follows that only for c=0 the root is non-complex. c/d is a reduced fraction, so that sin(c/dpi)=0 occurs for d=1, which is handled by our trivial case.
  521. if (this['s'] < 0) return null;
  522. // Now prime factor n and d
  523. var N = factorize(this['n']);
  524. var D = factorize(this['d']);
  525. // Exponentiate and take root for n and d individually
  526. var n = 1;
  527. var d = 1;
  528. for (var k in N) {
  529. if (k === '1') continue;
  530. if (k === '0') {
  531. n = 0;
  532. break;
  533. }
  534. N[k]*= P['n'];
  535. if (N[k] % P['d'] === 0) {
  536. N[k]/= P['d'];
  537. } else return null;
  538. n*= Math.pow(k, N[k]);
  539. }
  540. for (var k in D) {
  541. if (k === '1') continue;
  542. D[k]*= P['n'];
  543. if (D[k] % P['d'] === 0) {
  544. D[k]/= P['d'];
  545. } else return null;
  546. d*= Math.pow(k, D[k]);
  547. }
  548. if (P['s'] < 0) {
  549. return newFraction(d, n);
  550. }
  551. return newFraction(n, d);
  552. },
  553. /**
  554. * Check if two rational numbers are the same
  555. *
  556. * Ex: new Fraction(19.6).equals([98, 5]);
  557. **/
  558. "equals": function(a, b) {
  559. parse(a, b);
  560. return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"]; // Same as compare() === 0
  561. },
  562. /**
  563. * Check if two rational numbers are the same
  564. *
  565. * Ex: new Fraction(19.6).equals([98, 5]);
  566. **/
  567. "compare": function(a, b) {
  568. parse(a, b);
  569. var t = (this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"]);
  570. return (0 < t) - (t < 0);
  571. },
  572. "simplify": function(eps) {
  573. if (isNaN(this['n']) || isNaN(this['d'])) {
  574. return this;
  575. }
  576. eps = eps || 0.001;
  577. var thisABS = this['abs']();
  578. var cont = thisABS['toContinued']();
  579. for (var i = 1; i < cont.length; i++) {
  580. var s = newFraction(cont[i - 1], 1);
  581. for (var k = i - 2; k >= 0; k--) {
  582. s = s['inverse']()['add'](cont[k]);
  583. }
  584. if (Math.abs(s['sub'](thisABS).valueOf()) < eps) {
  585. return s['mul'](this['s']);
  586. }
  587. }
  588. return this;
  589. },
  590. /**
  591. * Check if two rational numbers are divisible
  592. *
  593. * Ex: new Fraction(19.6).divisible(1.5);
  594. */
  595. "divisible": function(a, b) {
  596. parse(a, b);
  597. return !(!(P["n"] * this["d"]) || ((this["n"] * P["d"]) % (P["n"] * this["d"])));
  598. },
  599. /**
  600. * Returns a decimal representation of the fraction
  601. *
  602. * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
  603. **/
  604. 'valueOf': function() {
  605. return this["s"] * this["n"] / this["d"];
  606. },
  607. /**
  608. * Returns a string-fraction representation of a Fraction object
  609. *
  610. * Ex: new Fraction("1.'3'").toFraction(true) => "4 1/3"
  611. **/
  612. 'toFraction': function(excludeWhole) {
  613. var whole, str = "";
  614. var n = this["n"];
  615. var d = this["d"];
  616. if (this["s"] < 0) {
  617. str+= '-';
  618. }
  619. if (d === 1) {
  620. str+= n;
  621. } else {
  622. if (excludeWhole && (whole = Math.floor(n / d)) > 0) {
  623. str+= whole;
  624. str+= " ";
  625. n%= d;
  626. }
  627. str+= n;
  628. str+= '/';
  629. str+= d;
  630. }
  631. return str;
  632. },
  633. /**
  634. * Returns a latex representation of a Fraction object
  635. *
  636. * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
  637. **/
  638. 'toLatex': function(excludeWhole) {
  639. var whole, str = "";
  640. var n = this["n"];
  641. var d = this["d"];
  642. if (this["s"] < 0) {
  643. str+= '-';
  644. }
  645. if (d === 1) {
  646. str+= n;
  647. } else {
  648. if (excludeWhole && (whole = Math.floor(n / d)) > 0) {
  649. str+= whole;
  650. n%= d;
  651. }
  652. str+= "\\frac{";
  653. str+= n;
  654. str+= '}{';
  655. str+= d;
  656. str+= '}';
  657. }
  658. return str;
  659. },
  660. /**
  661. * Returns an array of continued fraction elements
  662. *
  663. * Ex: new Fraction("7/8").toContinued() => [0,1,7]
  664. */
  665. 'toContinued': function() {
  666. var t;
  667. var a = this['n'];
  668. var b = this['d'];
  669. var res = [];
  670. if (isNaN(a) || isNaN(b)) {
  671. return res;
  672. }
  673. do {
  674. res.push(Math.floor(a / b));
  675. t = a % b;
  676. a = b;
  677. b = t;
  678. } while (a !== 1);
  679. return res;
  680. },
  681. /**
  682. * Creates a string representation of a fraction with all digits
  683. *
  684. * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
  685. **/
  686. 'toString': function(dec) {
  687. var N = this["n"];
  688. var D = this["d"];
  689. if (isNaN(N) || isNaN(D)) {
  690. return "NaN";
  691. }
  692. dec = dec || 15; // 15 = decimal places when no repetation
  693. var cycLen = cycleLen(N, D); // Cycle length
  694. var cycOff = cycleStart(N, D, cycLen); // Cycle start
  695. var str = this['s'] < 0 ? "-" : "";
  696. str+= N / D | 0;
  697. N%= D;
  698. N*= 10;
  699. if (N)
  700. str+= ".";
  701. if (cycLen) {
  702. for (var i = cycOff; i--;) {
  703. str+= N / D | 0;
  704. N%= D;
  705. N*= 10;
  706. }
  707. str+= "(";
  708. for (var i = cycLen; i--;) {
  709. str+= N / D | 0;
  710. N%= D;
  711. N*= 10;
  712. }
  713. str+= ")";
  714. } else {
  715. for (var i = dec; N && i--;) {
  716. str+= N / D | 0;
  717. N%= D;
  718. N*= 10;
  719. }
  720. }
  721. return str;
  722. }
  723. };