not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

779 lines
18 KiB

  1. /**
  2. * @license Fraction.js v3.3.1 09/09/2015
  3. * http://www.xarg.org/2014/03/rational-numbers-in-javascript/
  4. *
  5. * Copyright (c) 2015, Robert Eisele (robert@xarg.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 => <nominator>, 1 => <denominator> ]
  15. * [ n => <nominator>, 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. (function(root) {
  38. "use strict";
  39. // Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
  40. // Example: 1/7 = 0.(142857) has 6 repeating decimal places.
  41. // If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
  42. var MAX_CYCLE_LEN = 2000;
  43. // Parsed data to avoid calling "new" all the time
  44. var P = {
  45. "s": 1,
  46. "n": 0,
  47. "d": 1
  48. };
  49. function assign(n, s) {
  50. if (isNaN(n = parseInt(n, 10))) {
  51. throwInvalidParam();
  52. }
  53. return n * s;
  54. }
  55. function throwInvalidParam() {
  56. throw "Invalid Param";
  57. }
  58. var parse = function(p1, p2) {
  59. var n = 0, d = 1, s = 1;
  60. var v = 0, w = 0, x = 0, y = 1, z = 1;
  61. var A = 0, B = 1;
  62. var C = 1, D = 1;
  63. var N = 10000000;
  64. var M;
  65. if (p1 === undefined || p1 === null) {
  66. /* void */
  67. } else if (p2 !== undefined) {
  68. n = p1;
  69. d = p2;
  70. s = n * d;
  71. } else
  72. switch (typeof p1) {
  73. case "object":
  74. {
  75. if ("d" in p1 && "n" in p1) {
  76. n = p1["n"];
  77. d = p1["d"];
  78. if ("s" in p1)
  79. n*= p1["s"];
  80. } else if (0 in p1) {
  81. n = p1[0];
  82. if (1 in p1)
  83. d = p1[1];
  84. } else {
  85. throwInvalidParam();
  86. }
  87. s = n * d;
  88. break;
  89. }
  90. case "number":
  91. {
  92. if (p1 < 0) {
  93. s = p1;
  94. p1 = -p1;
  95. }
  96. if (p1 % 1 === 0) {
  97. n = p1;
  98. } else if (p1 > 0) { // check for != 0, scale would become NaN (log(0)), which converges really slow
  99. if (p1 >= 1) {
  100. z = Math.pow(10, Math.floor(1 + Math.log(p1) / Math.LN10));
  101. p1/= z;
  102. }
  103. // Using Farey Sequences
  104. // http://www.johndcook.com/blog/2010/10/20/best-rational-approximation/
  105. while (B <= N && D <= N) {
  106. M = (A + C) / (B + D);
  107. if (p1 === M) {
  108. if (B + D <= N) {
  109. n = A + C;
  110. d = B + D;
  111. } else if (D > B) {
  112. n = C;
  113. d = D;
  114. } else {
  115. n = A;
  116. d = B;
  117. }
  118. break;
  119. } else {
  120. if (p1 > M) {
  121. A+= C;
  122. B+= D;
  123. } else {
  124. C+= A;
  125. D+= B;
  126. }
  127. if (B > N) {
  128. n = C;
  129. d = D;
  130. } else {
  131. n = A;
  132. d = B;
  133. }
  134. }
  135. }
  136. n*= z;
  137. } else if (isNaN(p1) || isNaN(p2)) {
  138. d = n = NaN;
  139. }
  140. break;
  141. }
  142. case "string":
  143. {
  144. B = p1.match(/\d+|./g);
  145. if (B[A] === '-') {// Check for minus sign at the beginning
  146. s = -1;
  147. A++;
  148. } else if (B[A] === '+') {// Check for plus sign at the beginning
  149. A++;
  150. }
  151. if (B.length === A + 1) { // Check if it's just a simple number "1234"
  152. w = assign(B[A++], s);
  153. } else if (B[A + 1] === '.' || B[A] === '.') { // Check if it's a decimal number
  154. if (B[A] !== '.') { // Handle 0.5 and .5
  155. v = assign(B[A++], s);
  156. }
  157. A++;
  158. // Check for decimal places
  159. if (A + 1 === B.length || B[A + 1] === '(' && B[A + 3] === ')' || B[A + 1] === "'" && B[A + 3] === "'") {
  160. w = assign(B[A], s);
  161. y = Math.pow(10, B[A].length);
  162. A++;
  163. }
  164. // Check for repeating places
  165. if (B[A] === '(' && B[A + 2] === ')' || B[A] === "'" && B[A + 2] === "'") {
  166. x = assign(B[A + 1], s);
  167. z = Math.pow(10, B[A + 1].length) - 1;
  168. A+= 3;
  169. }
  170. } else if (B[A + 1] === '/' || B[A + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
  171. w = assign(B[A], s);
  172. y = assign(B[A + 2], 1);
  173. A+= 3;
  174. } else if (B[A + 3] === '/' && B[A + 1] === ' ') { // Check for a complex fraction "123 1/2"
  175. v = assign(B[A], s);
  176. w = assign(B[A + 2], s);
  177. y = assign(B[A + 4], 1);
  178. A+= 5;
  179. }
  180. if (B.length <= A) { // Check for more tokens on the stack
  181. d = y * z;
  182. s = /* void */
  183. n = x + d * v + z * w;
  184. break;
  185. }
  186. /* Fall through on error */
  187. }
  188. default:
  189. throwInvalidParam();
  190. }
  191. if (d === 0) {
  192. throw "DIV/0";
  193. }
  194. P["s"] = s < 0 ? -1 : 1;
  195. P["n"] = Math.abs(n);
  196. P["d"] = Math.abs(d);
  197. };
  198. var modpow = function(b, e, m) {
  199. for (var r = 1; e > 0; b = (b * b) % m, e >>= 1) {
  200. if (e & 1) {
  201. r = (r * b) % m;
  202. }
  203. }
  204. return r;
  205. };
  206. var cycleLen = function(n, d) {
  207. for (; d % 2 === 0;
  208. d/= 2) {}
  209. for (; d % 5 === 0;
  210. d/= 5) {}
  211. if (d === 1) // Catch non-cyclic numbers
  212. return 0;
  213. // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
  214. // 10^(d-1) % d == 1
  215. // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
  216. // as we want to translate the numbers to strings.
  217. var rem = 10 % d;
  218. for (var t = 1; rem !== 1; t++) {
  219. rem = rem * 10 % d;
  220. if (t > MAX_CYCLE_LEN)
  221. 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`
  222. }
  223. return t;
  224. };
  225. var cycleStart = function(n, d, len) {
  226. var rem1 = 1;
  227. var rem2 = modpow(10, len, d);
  228. for (var t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
  229. // Solve 10^s == 10^(s+t) (mod d)
  230. if (rem1 === rem2)
  231. return t;
  232. rem1 = rem1 * 10 % d;
  233. rem2 = rem2 * 10 % d;
  234. }
  235. return 0;
  236. };
  237. var gcd = function(a, b) {
  238. if (!a) return b;
  239. if (!b) return a;
  240. while (1) {
  241. a%= b;
  242. if (!a) return b;
  243. b%= a;
  244. if (!b) return a;
  245. }
  246. };
  247. /**
  248. * Module constructor
  249. *
  250. * @constructor
  251. * @param {number|Fraction} a
  252. * @param {number=} b
  253. */
  254. function Fraction(a, b) {
  255. if (!(this instanceof Fraction)) {
  256. return new Fraction(a, b);
  257. }
  258. parse(a, b);
  259. if (Fraction['REDUCE']) {
  260. a = gcd(P["d"], P["n"]); // Abuse a
  261. } else {
  262. a = 1;
  263. }
  264. this["s"] = P["s"];
  265. this["n"] = P["n"] / a;
  266. this["d"] = P["d"] / a;
  267. }
  268. /**
  269. * Boolean global variable to be able to disable automatic reduction of the fraction
  270. *
  271. */
  272. Fraction['REDUCE'] = 1;
  273. Fraction.prototype = {
  274. "s": 1,
  275. "n": 0,
  276. "d": 1,
  277. /**
  278. * Calculates the absolute value
  279. *
  280. * Ex: new Fraction(-4).abs() => 4
  281. **/
  282. "abs": function() {
  283. return new Fraction(this["n"], this["d"]);
  284. },
  285. /**
  286. * Inverts the sign of the current fraction
  287. *
  288. * Ex: new Fraction(-4).neg() => 4
  289. **/
  290. "neg": function() {
  291. return new Fraction(-this["s"] * this["n"], this["d"]);
  292. },
  293. /**
  294. * Adds two rational numbers
  295. *
  296. * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
  297. **/
  298. "add": function(a, b) {
  299. parse(a, b);
  300. return new Fraction(
  301. this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
  302. this["d"] * P["d"]
  303. );
  304. },
  305. /**
  306. * Subtracts two rational numbers
  307. *
  308. * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
  309. **/
  310. "sub": function(a, b) {
  311. parse(a, b);
  312. return new Fraction(
  313. this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
  314. this["d"] * P["d"]
  315. );
  316. },
  317. /**
  318. * Multiplies two rational numbers
  319. *
  320. * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
  321. **/
  322. "mul": function(a, b) {
  323. parse(a, b);
  324. return new Fraction(
  325. this["s"] * P["s"] * this["n"] * P["n"],
  326. this["d"] * P["d"]
  327. );
  328. },
  329. /**
  330. * Divides two rational numbers
  331. *
  332. * Ex: new Fraction("-17.(345)").inverse().div(3)
  333. **/
  334. "div": function(a, b) {
  335. parse(a, b);
  336. return new Fraction(
  337. this["s"] * P["s"] * this["n"] * P["d"],
  338. this["d"] * P["n"]
  339. );
  340. },
  341. /**
  342. * Clones the actual object
  343. *
  344. * Ex: new Fraction("-17.(345)").clone()
  345. **/
  346. "clone": function() {
  347. return new Fraction(this);
  348. },
  349. /**
  350. * Calculates the modulo of two rational numbers - a more precise fmod
  351. *
  352. * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
  353. **/
  354. "mod": function(a, b) {
  355. if (isNaN(this['n']) || isNaN(this['d'])) {
  356. return new Fraction(NaN);
  357. }
  358. if (a === undefined) {
  359. return new Fraction(this["s"] * this["n"] % this["d"], 1);
  360. }
  361. parse(a, b);
  362. if (0 === P["n"] && 0 === this["d"]) {
  363. Fraction(0, 0); // Throw div/0
  364. }
  365. /*
  366. * First silly attempt, kinda slow
  367. *
  368. return that["sub"]({
  369. "n": num["n"] * Math.floor((this.n / this.d) / (num.n / num.d)),
  370. "d": num["d"],
  371. "s": this["s"]
  372. });*/
  373. /*
  374. * New attempt: a1 / b1 = a2 / b2 * q + r
  375. * => b2 * a1 = a2 * b1 * q + b1 * b2 * r
  376. * => (b2 * a1 % a2 * b1) / (b1 * b2)
  377. */
  378. return new Fraction(
  379. (this["s"] * P["d"] * this["n"]) % (P["n"] * this["d"]),
  380. P["d"] * this["d"]
  381. );
  382. },
  383. /**
  384. * Calculates the fractional gcd of two rational numbers
  385. *
  386. * Ex: new Fraction(5,8).gcd(3,7) => 1/56
  387. */
  388. "gcd": function(a, b) {
  389. parse(a, b);
  390. // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)
  391. return new Fraction(gcd(P["n"], this["n"]), P["d"] * this["d"] / gcd(P["d"], this["d"]));
  392. },
  393. /**
  394. * Calculates the fractional lcm of two rational numbers
  395. *
  396. * Ex: new Fraction(5,8).lcm(3,7) => 15
  397. */
  398. "lcm": function(a, b) {
  399. parse(a, b);
  400. // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)
  401. if (P["n"] === 0 && this["n"] === 0) {
  402. return new Fraction;
  403. }
  404. return new Fraction(P["n"] * this["n"] / gcd(P["n"], this["n"]), gcd(P["d"], this["d"]));
  405. },
  406. /**
  407. * Calculates the ceil of a rational number
  408. *
  409. * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
  410. **/
  411. "ceil": function(places) {
  412. places = Math.pow(10, places || 0);
  413. if (isNaN(this["n"]) || isNaN(this["d"])) {
  414. return new Fraction(NaN);
  415. }
  416. return new Fraction(Math.ceil(places * this["s"] * this["n"] / this["d"]), places);
  417. },
  418. /**
  419. * Calculates the floor of a rational number
  420. *
  421. * Ex: new Fraction('4.(3)').floor() => (4 / 1)
  422. **/
  423. "floor": function(places) {
  424. places = Math.pow(10, places || 0);
  425. if (isNaN(this["n"]) || isNaN(this["d"])) {
  426. return new Fraction(NaN);
  427. }
  428. return new Fraction(Math.floor(places * this["s"] * this["n"] / this["d"]), places);
  429. },
  430. /**
  431. * Rounds a rational numbers
  432. *
  433. * Ex: new Fraction('4.(3)').round() => (4 / 1)
  434. **/
  435. "round": function(places) {
  436. places = Math.pow(10, places || 0);
  437. if (isNaN(this["n"]) || isNaN(this["d"])) {
  438. return new Fraction(NaN);
  439. }
  440. return new Fraction(Math.round(places * this["s"] * this["n"] / this["d"]), places);
  441. },
  442. /**
  443. * Gets the inverse of the fraction, means numerator and denumerator are exchanged
  444. *
  445. * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
  446. **/
  447. "inverse": function() {
  448. return new Fraction(this["s"] * this["d"], this["n"]);
  449. },
  450. /**
  451. * Calculates the fraction to some integer exponent
  452. *
  453. * Ex: new Fraction(-1,2).pow(-3) => -8
  454. */
  455. "pow": function(m) {
  456. if (m < 0) {
  457. return new Fraction(Math.pow(this['s'] * this["d"],-m), Math.pow(this["n"],-m));
  458. } else {
  459. return new Fraction(Math.pow(this['s'] * this["n"], m), Math.pow(this["d"], m));
  460. }
  461. },
  462. /**
  463. * Check if two rational numbers are the same
  464. *
  465. * Ex: new Fraction(19.6).equals([98, 5]);
  466. **/
  467. "equals": function(a, b) {
  468. parse(a, b);
  469. return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"]; // Same as compare() === 0
  470. },
  471. /**
  472. * Check if two rational numbers are the same
  473. *
  474. * Ex: new Fraction(19.6).equals([98, 5]);
  475. **/
  476. "compare": function(a, b) {
  477. parse(a, b);
  478. var t = (this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"]);
  479. return (0 < t) - (t < 0);
  480. },
  481. /**
  482. * Check if two rational numbers are divisible
  483. *
  484. * Ex: new Fraction(19.6).divisible(1.5);
  485. */
  486. "divisible": function(a, b) {
  487. parse(a, b);
  488. return !(!(P["n"] * this["d"]) || ((this["n"] * P["d"]) % (P["n"] * this["d"])));
  489. },
  490. /**
  491. * Returns a decimal representation of the fraction
  492. *
  493. * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
  494. **/
  495. 'valueOf': function() {
  496. return this["s"] * this["n"] / this["d"];
  497. },
  498. /**
  499. * Returns a string-fraction representation of a Fraction object
  500. *
  501. * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
  502. **/
  503. 'toFraction': function(excludeWhole) {
  504. var whole, str = "";
  505. var n = this["n"];
  506. var d = this["d"];
  507. if (this["s"] < 0) {
  508. str+= '-';
  509. }
  510. if (d === 1) {
  511. str+= n;
  512. } else {
  513. if (excludeWhole && (whole = Math.floor(n / d)) > 0) {
  514. str+= whole;
  515. str+= " ";
  516. n%= d;
  517. }
  518. str+= n;
  519. str+= '/';
  520. str+= d;
  521. }
  522. return str;
  523. },
  524. /**
  525. * Returns a latex representation of a Fraction object
  526. *
  527. * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
  528. **/
  529. 'toLatex': function(excludeWhole) {
  530. var whole, str = "";
  531. var n = this["n"];
  532. var d = this["d"];
  533. if (this["s"] < 0) {
  534. str+= '-';
  535. }
  536. if (d === 1) {
  537. str+= n;
  538. } else {
  539. if (excludeWhole && (whole = Math.floor(n / d)) > 0) {
  540. str+= whole;
  541. n%= d;
  542. }
  543. str+= "\\frac{";
  544. str+= n;
  545. str+= '}{';
  546. str+= d;
  547. str+= '}';
  548. }
  549. return str;
  550. },
  551. /**
  552. * Returns an array of continued fraction elements
  553. *
  554. * Ex: new Fraction("7/8").toContinued() => [0,1,7]
  555. */
  556. 'toContinued': function() {
  557. var t;
  558. var a = this['n'];
  559. var b = this['d'];
  560. var res = [];
  561. do {
  562. res.push(Math.floor(a / b));
  563. t = a % b;
  564. a = b;
  565. b = t;
  566. } while (a !== 1);
  567. return res;
  568. },
  569. /**
  570. * Creates a string representation of a fraction with all digits
  571. *
  572. * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
  573. **/
  574. 'toString': function() {
  575. var g;
  576. var N = this["n"];
  577. var D = this["d"];
  578. if (isNaN(N) || isNaN(D)) {
  579. return "NaN";
  580. }
  581. if (!Fraction['REDUCE']) {
  582. g = gcd(N, D);
  583. N/= g;
  584. D/= g;
  585. }
  586. var p = String(N).split(""); // Numerator chars
  587. var t = 0; // Tmp var
  588. var ret = [~this["s"] ? "" : "-", "", ""]; // Return array, [0] is zero sign, [1] before comma, [2] after
  589. var zeros = ""; // Collection variable for zeros
  590. var cycLen = cycleLen(N, D); // Cycle length
  591. var cycOff = cycleStart(N, D, cycLen); // Cycle start
  592. var j = -1;
  593. var n = 1; // str index
  594. // rough estimate to fill zeros
  595. var length = 15 + cycLen + cycOff + p.length; // 15 = decimal places when no repitation
  596. for (var i = 0; i < length; i++, t*= 10) {
  597. if (i < p.length) {
  598. t+= Number(p[i]);
  599. } else {
  600. n = 2;
  601. j++; // Start now => after comma
  602. }
  603. if (cycLen > 0) { // If we have a repeating part
  604. if (j === cycOff) {
  605. ret[n]+= zeros + "(";
  606. zeros = "";
  607. } else if (j === cycLen + cycOff) {
  608. ret[n]+= zeros + ")";
  609. break;
  610. }
  611. }
  612. if (t >= D) {
  613. ret[n]+= zeros + ((t / D) | 0); // Flush zeros, Add current digit
  614. zeros = "";
  615. t = t % D;
  616. } else if (n > 1) { // Add zeros to the zero buffer
  617. zeros+= "0";
  618. } else if (ret[n]) { // If before comma, add zero only if already something was added
  619. ret[n]+= "0";
  620. }
  621. }
  622. // If it's empty, it's a leading zero only
  623. ret[0]+= ret[1] || "0";
  624. // If there is something after the comma, add the comma sign
  625. if (ret[2]) {
  626. return ret[0] + "." + ret[2];
  627. }
  628. return ret[0];
  629. }
  630. };
  631. if (typeof define === "function" && define["amd"]) {
  632. define([], function() {
  633. return Fraction;
  634. });
  635. } else if (typeof exports === "object") {
  636. module["exports"] = Fraction;
  637. } else {
  638. root['Fraction'] = Fraction;
  639. }
  640. })(this);